简体   繁体   English

为什么Scala在已经具有特征的情况下会有课程?

[英]Why does Scala have classes when it already has traits?

This may seem like a silly question, so bear with me... 这似乎是一个愚蠢的问题,所以请耐心等待......

Consider this REPL session: 考虑一下这个REPL会话:

scala> trait T
defined trait T

scala> val t = new T
<console>:8: error: trait T is abstract; cannot be instantiated
       val t = new T
               ^

scala> val t = new T {}
t: java.lang.Object with T = $anon$1@78db81f3

scala> class C
defined class C

scala> val c = new C
c: C = C@170a6001

We can use a trait just like a class, except that we have to add {} after the new T . 我们可以像使用类一样使用特征,除了我们必须在new T之后添加{} In fact, we're essentially mixing T into java.lang.Object , which actually makes a lot of sense to me. 实际上,我们实际上是将T混合到java.lang.Object ,这实际上对我来说很有意义。

If we have members, again only the {} must be added: 如果我们有成员,则只需添加{}

scala> trait T2 { val s = "test" }
defined trait T2

scala> val t2 = new T2
<console>:8: error: trait T2 is abstract; cannot be instantiated
       val t2 = new T2
                ^

scala> val t2 = new T2 {}
t2: java.lang.Object with T2 = $anon$1@6a688d6f

scala> t2.s
res0: java.lang.String = test

scala> class C2 { val s = "test" }
defined class C2

scala> val c2 = new C2
c2: C2 = C2@73ea7821

scala> c2.s
res1: java.lang.String = test

If we have abstract members then the trait declaration is actually shorter by a few characters and, more importantly, more consistent in my eyes (no need to remember to put abstract in front of your declarations): 如果我们有抽象成员,那么特征声明实际上会缩短几个字符,更重要的是,在我眼中更加一致(不需要记住在你的声明前放置abstract ):

scala> trait T3 { val s: String }
defined trait T3

scala> val t3 = new T3 { val s = "test" }
t3: java.lang.Object with T3 = $anon$1@1f2f0ce9

scala> abstract class C3 { val s: String }
defined class C3

scala> val c3 = new C3 { val s = "test" }
c3: C3 = $anon$1@207a8313

If you forget that you must define some of the members, both ways give you compile errors: 如果您忘记必须定义一些成员,则两种方式都会给您编译错误:

scala> val badt3 = new T3 {}
<console>:7: error: object creation impossible, since value s in trait T3 of type String is not defined
       val badt3 = new T3 {}

scala> class BadC3 { val s: String }
<console>:8: error: class BadC3 needs to be abstract, since value s is not defined
       class BadC3 { val s: String }

And if we try to do more complex things then the power of traits naturally becomes further apparent: 如果我们尝试做更复杂的事情,那么特质的力量自然会变得更加明显:

scala> val t4 = new T with T2
t4: java.lang.Object with T with T2 = $anon$1@479e0994

scala> val c4 = new C with C2
<console>:9: error: class C2 needs to be a trait to be mixed in
       val c4 = new C with C2

So again I ask, why does Scala bother with classes at all when traits are apparently both simpler and more powerful? 所以我再次问,为什么当特征显然更简单,更强大时,Scala会对课程感到困扰?

I assume the reason is conceptual and actual compatibility with Java, but I wonder whether code compatability could have been maintained behind the scenes. 我假设原因是与Java的概念和实际兼容性,但我想知道是否可以在幕后维护代码兼容性。 As I understand it, Scala traits just become Java classes behind the scenes, so why couldn't the reverse happen and Scala consider Java classes to essentially be traits? 据我所知,Scala特性只是在幕后成为Java类,为什么不能反过来发生Scala认为Java类本质上是特征?

Related to all this, why not allow dropping the curly brackets when unnecessary? 与此相关,为什么不允许在不必要时删除大括号? For example: 例如:

val t = new T

At that point, as a user , traits would be indistinguishable from current Scala classes, but of course better. 此时, 作为用户 ,特征与当前的Scala类无法区分,但当然更好。

There are several differences between traits and classes: 特征和类之间有几个不同之处:

  • a trait can not take constructor parameters. 特征不能采用构造函数参数。 This limitation might be lifted at some point, but it's a hard problem. 这个限制可能会在某个时候解除,但这是一个难题。 A trait may be inherited multiple times in a hierarchy, and each instantiation may give different values for the constructor parameters 特征可以在层次结构中多次继承,并且每个实例化可以为构造函数参数提供不同的值

  • a trait is compiled to a Java interface and an implementation class (carrying the concrete methods). 特征被编译为Java接口和实现类(携带具体方法)。 This means it's a bit slower, because all calls go through interfaces, and if they're concrete, they are forwarded to their implementation 这意味着它有点慢,因为所有调用都通过接口,如果它们是具体的,它们将被转发到它们的实现

  • a trait with concrete members can't be nicely inherited in Java (it could, but it would look like an interface, therefore concrete members would still need to be implemented in Java). 具体成员的特征不能在Java中很好地继承(它可以,但它看起来像一个接口,因此具体成员仍然需要用Java实现)。

I don't think the distinction between classes and traits will go away, mostly because of the last two items. 我不认为类和特征之间的区别会消失,主要是因为最后两项。 But they may become easier to use if the first point is solved. 但如果第一点得到解决,它们可能会变得更容易使用。 Regarding instantiation without the {} , that's a convenience that could be added, but I personally wouldn't like it: each instantiation creates a new class (an anonymous one), and there should be an indication to the programmer that that's the case. 关于没有{}实例化,这是一个可以添加的便利,但我个人不喜欢它:每个实例化创建一个新 (一个匿名的),并且应该向程序员表明情况就是这样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM