È possibile riscrivere trait B
con (più avanti sulla tua obiettivo, che, penso, è un po 'diverso)
trait B extends A {
type MyType <: BInner with AInner
}
E questo ha perfettamente senso. Un valore di tipo B#MyType
può essere visto come BInner
o AInner
.
Non è necessario ripetere Abstract
perché A
è già una sottoclasse di Abstract
. Non è necessario scrivere override
poiché ciò è implicito per una dichiarazione di tipo. Quindi la domanda è: perché A#MyType
non funziona come AInner
?
Ecco cosa dice lo scala linguaggio spec. Sui tipi volatili.
3.6 Volatile Types
Type volatility approximates the possibility that a type parameter or abstract type instance of a type does not have any non-null values. As explained in (§3.1), a value member of a volatile type cannot appear in a path. A type is volatile if it falls into one of four categories: A compound type T1 with ... with Tn {R } is volatile if one of the following two conditions hold. 1. One of T2, ..., Tn is a type parameter or abstract type, or 2. T1 is an abstract type and and either the refinement R or a type Tj for j > 1 contributes an abstract member to the compound type, or 3. one of T1, ..., Tn is a singleton type. Here, a type S contributes an abstract member to a type T if S contains an abstract member that is also a member of T . A refinement R contributes an abstract member to a type T if R contains an abstract declaration which is also a member of T . A type designator is volatile if it is an alias of a volatile type, or if it designates a type parameter or abstract type that has a volatile type as its upper bound. A singleton type p.type is volatile, if the underlying type of path p is volatile. An existential type T forSome {Q } is volatile if T is volatile.
Altro importante elemento di cui parla la specifica è di circa tipo prioritario astratto:
Another restriction applies to abstract type members: An abstract type member with a volatile type (§3.6) as its upper bound may not override an abstract type member which does not have a volatile upper bound.
L'errore del compilatore è:
error: overriding type MyType in trait A with bounds <: AInner;
type MyType is a volatile type; cannot override a type with non-volatile upper bound
Ciò è coerente con la specifica. BInner with A#MyType
è volatile. Prima che MyType
avessero un non volatile come Any
.
Il problema è che un tipo nel sistema di tipo scala deve avere un significato univoco. Un tipo astratto può essere pensato come un tipo la cui dichiarazione è rinviata ad una sottoclasse.Quindi non c'è alcun problema nel dichiarare valori di un tipo astratto quando è ancora astratto. D'altra parte, se abbiamo un tipo come BInner with A#MyType
, questo tipo potrebbe avere diversi significati. Si chiama volatile e non ha senso avere un valore non nullo di questo tipo, in quanto potrebbe avere tanti tipi di sottoclassi che istanziano il tipo astratto MyType
. Per semplificare le cose, potremmo pensare a un tipo volatile come un tipo non essendo un sottotipo di Any
(e volatile come sottotipo Any
). Abbiamo quindi una contraddizione che il compilatore menziona.
Tornando al vostro obiettivo, che lei ha affermato come
What I'm trying to achieve here(in trait B) is to further restrict the type MyType declared > in Abstract, so any value of type MyType must extend all the MyTypes in the mixin tree.
È possibile raggiungere questo obiettivo grazie a caratteristiche interne di questo tipo.
trait Abstract {
type MyType
}
trait B extends Abstract {
trait MyType {
def bMethod : Int
}
}
trait A extends B {
trait MyType extends super.MyType {
}
}
Beh, spero che questo sia ciò che stai cercando.
Perché anche i membri del metodo astratto sono vietati nei tipi non volatili? – Blaisorblade