我在google中尝试了许多解决方案,包括:多个构造函数,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,是的,它必须是一个类。
目标是拥有一个扩展ImageView的CustomView,所有这些都可以工作:
new CustomView(context)
new CustomView(context,attribute set)
new CustomView(context,attribute set, style)
如果您需要进一步澄清这个棘手的问题,请告诉我!
答案 0 :(得分:7)
根据Martin Odersky(Scala的创建者)的说法,这是不可能的。
“有没有办法在不同的地方调用不同的超级构造函数 class-constructors - 或者只需要进入main-constructor 支持一个超级构造函数?
不,它必须通过主构造函数。这是一个细节 Scala比Java更具限制性。“
我认为你最好的方法是用Java实现你的观点。
答案 1 :(得分:5)
听起来你可能只是更好地在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 def imageViewToCustomView(view: ImageView) = new {
def foo = ...
}
答案 2 :(得分:1)
这个问题引出了一些我想与您分享的设计考虑因素。
我首先要考虑的是,如果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
}
答案 3 :(得分:0)
根据Android View
documentation 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)
的{{1}},则会更复杂一些。 Java protected methods cannot be called from traits.解决方法是在trait
和class
实施中实施访问者:
object