Почему Scala имеет классы, когда у него уже есть черты?
Это может показаться глупым вопросом, так что несите меня...
Рассмотрим этот сеанс 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 = [email protected]
scala> class C
defined class C
scala> val c = new C
c: C = [email protected]
Мы можем использовать черту как класс, за исключением того, что после new T
мы должны добавить {}
. Фактически, мы по существу смешиваем T
в java.lang.Object
, что на самом деле имеет для меня большой смысл.
Если у нас есть члены, снова нужно добавить только {}
:
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 = [email protected]
scala> t2.s
res0: java.lang.String = test
scala> class C2 { val s = "test" }
defined class C2
scala> val c2 = new C2
c2: C2 = [email protected]
scala> c2.s
res1: java.lang.String = test
Если у нас есть абстрактные члены, то декларация признака на самом деле короче несколькими символами и, что более важно, более последовательна в моих глазах (не нужно забывать ставить abstract
перед вашими объявлениями):
scala> trait T3 { val s: String }
defined trait T3
scala> val t3 = new T3 { val s = "test" }
t3: java.lang.Object with T3 = [email protected]
scala> abstract class C3 { val s: String }
defined class C3
scala> val c3 = new C3 { val s = "test" }
c3: C3 = [email protected]
Если вы забыли, что вы должны определить некоторые из членов, оба способа дают вам ошибки компиляции:
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 }
И если мы попытаемся сделать более сложные вещи, тогда сила черт естественно станет более очевидной:
scala> val t4 = new T with T2
t4: java.lang.Object with T with T2 = [email protected]
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
Итак, я спрашиваю, почему Scala вообще не разбирается в классах, когда черты, по-видимому, более простые и мощные?
Я предполагаю, что причина в концептуальной и фактической совместимости с Java, но мне интересно, можно ли поддерживать совместимость кода за кулисами. Насколько я понимаю, Scala черты просто становятся Java-классами за кулисами, так почему бы не произойти обратное и Scala считать классы Java по существу чертами?
Что касается всего этого, почему бы не разрешить сбросить фигурные скобки, когда это не нужно? Например:
val t = new T
В этот момент, как пользователь, черты будут неотличимы от текущих классов Scala, но, конечно, лучше.
Ответы
Ответ 1
Существует несколько различий между чертами и классами:
-
признак не может принимать параметры конструктора. Это ограничение может быть снято в какой-то момент, но это сложная проблема. Характеристика может быть унаследована несколько раз в иерархии, и каждый экземпляр может давать разные значения для параметров конструктора
-
признак скомпилирован для интерфейса Java и класса реализации (с использованием конкретных методов). Это означает, что это немного медленнее, потому что все вызовы проходят через интерфейсы, и если они конкретны, они перенаправляются на их реализацию.
-
черта с конкретными членами не может быть красиво унаследована в Java (это может быть, но это будет выглядеть как интерфейс, поэтому конкретные члены все равно должны быть реализованы на Java).
Я не думаю, что различие между классами и чертами исчезнет, главным образом из-за последних двух предметов. Но они могут стать проще использовать, если первая точка решена. Что касается создания экземпляра без {}
, это удобство, которое можно было бы добавить, но мне лично это не понравилось: каждый экземпляр создает новый класс (анонимный), и должно быть указание программисту, что это дело.