为什么我们需要scala中的特性?

为什么我们需要scala中的特性?,scala,traits,Scala,Traits,因此,我试图制作一个欺骗服务器,与sentry交谈(不重要),偶然发现了一个案例,我需要同时继承两个类(不是traits),让我们称它们为class SentryHandler Extendes Handler和class TwitterHandler Extendes Handler,并假设,我需要创建MyHandler,它继承了它们 经过一段愚蠢的时间,当我认为不使用可怕的“授权模式”是不可能的时候,我找到了一个解决方案: trait SentryTrait extends SentryHa

因此,我试图制作一个欺骗服务器,与sentry交谈(不重要),偶然发现了一个案例,我需要同时继承两个类(不是traits),让我们称它们为
class SentryHandler Extendes Handler
class TwitterHandler Extendes Handler
,并假设,我需要创建
MyHandler
,它继承了它们

经过一段愚蠢的时间,当我认为不使用可怕的“授权模式”是不可能的时候,我找到了一个解决方案:

trait SentryTrait extends SentryHandler
class MyHandler extends TwitterHandler with SentryTrait
现在,这让我思考:拥有“特质”这个概念的目的是什么?如果这个想法是为了强制你可以继承多个trait,但只能继承一个类,那么这看起来非常容易。这听起来有点像
class
应该是继承的“主线”(即你“扩展了一个带有特征的类”,但这也不是真的:你可以
扩展一个带有(或没有)一堆其他特征的特征,而根本没有类

您不能实例化一个trait,但对于抽象类也是如此

我能想到的唯一真正的区别是,一个特征不能有构造函数参数。但这有什么意义呢? 我是说,为什么不?像这样的事情会有什么问题

class Foo(bar: String, baz: String) extends Bar(bar) with Baz(baz) 
您的解决方案(如果我理解正确的话)不起作用。您不能在scala中多重继承类:

scala> class Handler
defined class Handler

scala> class SentryHandler extends Handler
defined class SentryHandler

scala> class TwitterHandler extends Handler
defined class TwitterHandler

scala> trait SentryTrait extends SentryHandler
defined trait SentryTrait

scala> class MyHandler extends TwitterHandler with SentryTrait
<console>:11: error: illegal inheritance; superclass TwitterHandler
 is not a subclass of the superclass SentryHandler
 of the mixin trait SentryTrait
       class MyHandler extends TwitterHandler with SentryTrait
即使trait
A
super是
Base
(对于
T1
),它调用
B
实现,而不是
Base
。这是由于

所以对于类来说,如果你扩展了一些东西,你可以确定下一步会调用这个基。但是对于trait来说,这是不正确的。这可能就是为什么你没有trait构造函数参数的原因

我能想到的唯一真正的区别是一个特征不能有构造函数参数。但是这有什么意义呢?我的意思是,为什么不呢

考虑

trait A(val x: Int)
trait B extends A(1)
trait C extends A(2)
class D extends B with C

什么应该
(新的D{}).x
be?注意:Scala 3中有,但仍然有限制,因此不允许使用上述内容。

问题应该是:为什么我们需要Scala中的类?Martin Odersky说过Scala可以只使用traits。我们需要向traits添加构造函数,以便构造traits的实例。没关系,Odersky说他已经为特征构造器设计了一个线性化算法

真正的目的是平台互操作性

Scala打算与几个平台集成(目前是Java,以前是.NET,将来可能是Cocoa/Core Foundation/Swift/Objective-C)拥有不同的类概念,Scala特性和平台类之间的1:1映射并不总是容易的。例如,这与接口不同:平台接口和Scala特性之间的映射很小——只有抽象成员的特性同构于接口

类、包和
null
是Scala特性的一些示例,其主要目的是平台集成

Scala的设计者们非常努力地保持语言的小型化、简单化和正交性。但是Scala也明确地打算与现有的平台很好地集成。事实上,尽管Scala本身就是一种优秀的语言,但它是专门设计来替代主要的平台语言的(Java平台上的Java,.NET平台上的C#)。为了做到这一点,必须做出一些妥协:

  • Scala有类,尽管它们与traits是冗余的(假设我们为traits添加了构造函数),因为将Scala类映射到平台类很容易,而将traits映射到平台类几乎是不可能的。只要看看Scala为了将traits编译成高效的JVM字节码而必须跳过的环。(对于每个trait,都有一个包含API的接口和一个包含方法的静态类。对于该trait混入的每个类,都会生成一个转发器类,将对trait方法的方法调用转发给属于该trait的静态类。)
  • Scala有包,即使它们与对象是冗余的。Scala包可以简单地映射到Java包和.NET名称空间。对象不能
  • 包对象是克服包的一些限制的一种方法,如果没有包,我们就不需要包对象
  • 类型擦除。编译到JVM时,完全可以保留泛型类型,例如,您可以将它们存储在注释中。但是第三方Java库无论如何都会擦除它们的类型,其他语言也不会理解注释并将Scala类型视为擦除,因此您无论如何都必须处理类型擦除,并且如果你不得不这么做,那为什么两者都要做呢
  • 当然是
    null
    。在与现实世界的Java代码交互时,不可能以任何合理的方式在
    null
    Option
    之间自动映射。您必须在Scala中使用
    null
    ,尽管我们希望它不存在

在trait(从而使其成为类)中包含构造函数和状态的问题在于多重继承。虽然在假设语言中这在技术上是可能的,但这对语言定义和理解程序代码来说是可怕的。菱形问题,在对该问题的其他回答中提到),导致调用最高级别的基类构造函数两次(下例中的构造函数)

以类似Scala语言的代码为例,它允许多重继承:

Class A(val x: Int)
class B extends A(1)
class C extends A(2)
class D extends B, C
如果包含state,那么您必须在类A中有两个值x的副本。因此您有两个类A的副本(或者一个副本和菱形问题-由于UML继承图的菱形而被称为菱形)

C++编译器的早期版本(称为C前线)

Class A(val x: Int)
class B extends A(1)
class C extends A(2)
class D extends B, C