我通过关键字在谷歌尝试了许多解决方案:多个构造函数,scala,继承,子类。
似乎没有人适合这种场合。 ImageView
有三个构造函数:
ImageView(context)
ImageView(context,attribute set)
ImageView(context,attribute set, style)
在scala中,您只能扩展其中一个。并使用更完整的构造函数的解决方案(ImageView(context,attribute set, style)
并且传递默认值也不起作用,因为构造函数 ImageView(context)
做了一些与其他两个构造函数完全不同的东西。
使用特征或伴随对象的一些解决方案似乎不起作用,因为CustomView必须是一个类!我的意思是我不是唯一一个使用这个类的人(因此我可以按照我想要的任何方式编写scala代码)还有使用这个类的android-sdk,是的,它必须是一个类。
target是一个扩展ImageView的CustomView,所有这些工作:
new CustomView(context)
new CustomView(context,attribute set)
new CustomView(context,attribute set, style)
如果您需要进一步澄清这个棘手的事情,请告诉我!
根据 Android的 View
文件 View(Context)
从代码构造时使用 View(Context, AttributeSet)
和 View(Context, AttributeSet, int)
(以及API级别21 View(Context, AttributeSet, int, int)
)当使用时 View
从XML膨胀。
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)
(不是真正的构造函数)。
而另一个曾经喜欢:
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 {}
第四个构造函数可以像:
var myView4 = new MyView(context, attrs, defStyle, defStyleRes)
更新:
如果你试着打电话给它会有点复杂 protected
方法 View
, 喜欢 setMeasuredDimension(int, int)
来自 trait
。 无法从特征中调用Java保护的方法。 解决方法是在中实现一个访问器 class
和 object
实现:
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)
}
}
根据马丁奥德斯基(Scala的创造者)的说法,这是不可能的。
在 http://scala-programming-language.1934581.n4.nabble.com/scala-calling-different-super-constructors-td1994456.html:
“有没有办法在不同的范围内调用不同的超级构造函数
class-constructors - 或者只需要进入main-constructor
支持一个超级构造函数?
不,它必须通过主构造函数。这是一个细节
Scala比Java更具限制性。“
我认为你最好的方法是用Java实现你的观点。
听起来你可能只是更好地在Java中编写子类。否则,如果您对使用三参数构造函数的SDK的假设是正确的,那么您可以使用特征和具有伴随对象的类。 SDK将使用三个参数构造函数 CustomView
它还实现了包含您需要的任何其他行为的特征:
trait TCustomView {
// additional behavior here
}
final class CustomView(context: Context, attributes: AttributeSet, style: Int)
extends ImageView(context, attributes, style) with TCustomView
在应用程序代码中,您可以通过以下方式使用一个,两个或三个参数版本:
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添加其他行为:
implicit def imageViewToCustomView(view: ImageView) = new {
def foo = ...
}
这个问题让我想到几个设计考虑因素,我想与大家分享。
我首先要考虑的是,如果Java类已经被正确设计,多个构造函数的可用性应该是某些类属性可能具有这一事实的标志。 默认 值。
Scala提供 默认值 作为一个 语言功能,因此您不必费心提供多个构造函数,您可以简单地为构造函数的某些参数提供默认值。这种方法导致了 更干净的API 三个不同的构造函数产生这种行为,因为你明确了为什么你不需要指定所有的参数。
我的第二个考虑因素是,如果您正在扩展第三方库的类,这并不总是适用。然而:
- 如果要扩展一个开源库的类并且该类设计正确,则可以简单地研究构造函数参数的默认值
如果要扩展未正确设计的类(即,重载构造函数不是默认某些参数的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
}