11

複数のコンストラクター、スカラ、継承、サブクラスというキーワードでグーグルで見つかった多くの解決策を試しました。

この機会にうまくいくものはないようです。ImageViewには 3 つのコンストラクタがあります。

ImageView(context)
ImageView(context,attribute set)
ImageView(context,attribute set, style)

scala では、拡張できるのはそのうちの 1 つだけです。また、より完全なコンストラクター ( ImageView(context,attribute set, style)) を使用してデフォルト値を渡すという解決策も機能しません。これは、コンストラクターImageView(context)が他の 2 つのコンストラクターとはまったく異なることを行うためです。

CustomView はクラスでなければならないため、トレイトまたはコンパニオン オブジェクトを使用する一部のソリューションは機能しないようです。つまり、このクラスを使用するのは私だけではありません (したがって、好きなように scala コードを書くことができます)。このクラスを使用する android-sdk もあり、はい、それはクラスである必要があります。

ターゲットは、ImageView を拡張する CustomView を持つことであり、これらすべてが機能します。

new CustomView(context)
new CustomView(context,attribute set)
new CustomView(context,attribute set, style)

このトリッキーな問題についてさらに説明が必要な場合はお知らせください。

4

4 に答える 4

7

Martin Odersky (Scala の作成者) によると、それは不可能です。

http://scala-programming-language.1934581.n4.nabble.com/scala-calling-different-super-constructors-td1994456.html : _

「異なるクラスコンストラクター内で異なるスーパーコンストラクターを呼び出す方法はありますか?または、すべてがメインコンストラクターに到達する必要があり、1つのスーパーコンストラクターのみがサポートされていますか?

いいえ、メイン コンストラクターを経由する必要があります。これは、Scala が Java よりも制限が厳しい 1 つの詳細です。」

あなたの最善のアプローチは、Java でビューを実装することだと思います。

于 2012-12-16T16:27:32.087 に答える
5

Java でサブクラスを作成した方がよいように思えます。それ以外の場合は、3 つの引数のコンストラクターを使用する SDK に関する想定が正しければ、コンパニオン オブジェクトと共にトレイトとクラスを使用できます。CustomViewSDK は、必要な追加の動作を含むトレイトも実装する3 つの引数コンストラクターを使用します。

trait TCustomView {
  // additional behavior here
}

final class CustomView(context: Context, attributes: AttributeSet, style: Int)
  extends ImageView(context, attributes, style) with TCustomView

アプリケーション コードでは、1 つ、2 つ、または 3 つの引数バージョンを次のように使用できます。

object CustomView {
  def apply(c: Context, a: AttributeSet, s: Int) =
    new ImageView(c, a, s) with TCustomView
  def apply(context: Context, attributes: AttributeSet) =
    new ImageView(context, attributes) with TCustomView
  def apply(context: Context) = new ImageView(context) with TCustomView
}

CustomView(context)
CustomView(context, attributes)
CustomView(context, attributes, style)

大変な作業のようです。目標によっては、暗黙的な動作を追加できる場合があります。

implicit def imageViewToCustomView(view: ImageView) = new {
  def foo = ...
}
于 2012-12-12T17:53:18.667 に答える
1

この質問は、私があなたと共有したいいくつかの設計上の考慮事項に私を導きました.

私の最初の考慮事項は、Java クラスが正しく設計されている場合、複数のコンストラクターが使用可能であることは、一部のクラス プロパティにデフォルト値が設定されている可能性があることを示しているはずだということです。

Scala は言語機能としてデフォルト値を提供するため、複数のコンストラクターを提供することを気にする必要はまったくありません。コンストラクターのいくつかの引数にデフォルト値を提供するだけで済みます。このアプローチは、すべてのパラメーターを指定する必要がない理由を明確にするため、この種の動作を生成する 3 つの異なるコンストラクターよりもはるかにクリーンな APIにつながります。

2 つ目の考慮事項は、サードパーティ ライブラリのクラスを拡張している場合、これが常に当てはまるとは限らないということです。でも:

  • オープン ソース ライブラリのクラスを拡張していて、そのクラスが正しく設計されている場合は、コンストラクター引数のデフォルト値を簡単に調べることができます。
  • 正しく設計されていないクラスを拡張する場合 (つまり、オーバーロードされたコンストラクターが一部のパラメーターをデフォルトにする API ではない場合)、またはソースにアクセスできない場合は、継承を構成に置き換えて、暗黙的な変換を提供できます。

    class CustomView(c: Context, a: Option[AttributeSet]=None, s: Option[Int]=None){
    
       private val underlyingView:ImageView = if(a.isDefined)
                                                if (s.isDefined)
                                                    new ImageView(c,a.get,s.get)
                                                else
                                                    new ImageView(c,a.get)
                                            else
                                                new ImageView(c)
    }
    
    object CustomView {
        implicit def asImageView(customView:CustomView):ImageView = customView.underlyingView
    }
    
于 2012-12-20T10:52:51.030 に答える
0

AndroidのViewドキュメント によると、View(Context)はコードから構築されたときに使用されView(Context, AttributeSet)View(Context, AttributeSet, int)(API レベル 21 以降View(Context, AttributeSet, int, int)) はViewXML からインフレートされたときに使用されます。

XML コンストラクターはすべて同じコンストラクターを呼び出すだけで、ほとんどの引数を持つコンストラクターが実際の実装を持つ唯一のコンストラクターであるため、Scala でデフォルトの引数を使用できます。一方、「コード コンストラクター」は別の実装を持っている可能性があるため、実際には Scala からも呼び出した方がよいでしょう。

次の実装が解決策になる可能性があります。

private trait MyViewTrait extends View {
  // implementation
} 

class MyView(context: Context, attrs: AttributeSet, defStyle: Int = 0)
    extends View(context, attrs, defStyle) with MyViewTrait {}

object MyView {
  def apply(context: Context) = new View(context) with MyViewTrait
}

「コードコンストラクター」は、次のように使用できます。

var myView = MyView(context)

(実際のコンストラクターではありません)。

そして、もう1つは次のようになります。

var myView2 = new MyView(context, attrs)
var myView3 = new MyView(context, attrs, defStyle)

これは、SDK が期待する方法です。

同様に、API レベル 21 以降では、次のclassように定義できます。

class MyView(context: Context, attrs: AttributeSet, defStyle: Int = 0, defStyleRes: Int = 0)
    extends View(context, attrs, defStyle, defStyleRes) with MyViewTrait {}

4 番目のコンストラクターは次のように使用できます。

var myView4 = new MyView(context, attrs, defStyle, defStyleRes)

アップデート:

からのように でprotectedメソッドを呼び出そうとすると、少し複雑になります。Java 保護メソッドは、特性から呼び出すことはできません。回避策は、 と の実装にアクセサーを実装することです。ViewsetMeasuredDimension(int, int)traitclassobject

private trait MyViewTrait extends View {
  protected def setMeasuredDimensionAccessor(w: Int, h: Int): Unit
  def callingSetMeasuredDimensionAccessor(): Unit = {
    setMeasuredDimensionAccessor(1, 2)
  }
} 

class MyView(context: Context, attrs: AttributeSet, defStyle: Int = 0)
    extends View(context, attrs, defStyle) with MyViewTrait {
  override protected def setMeasuredDimensionAccessor(w: Int, h: Int) =
    setMeasuredDimension(w, h)
}

object MyView {
  def apply(context: Context) = new View(context) with MyViewTrait {
    override protected def setMeasuredDimensionAccessor(w: Int, h: Int) =
      setMeasuredDimension(w, h)
  }
}
于 2015-08-04T20:02:05.713 に答える