官术网_书友最值得收藏!

Type members

A type member is similar to the type parameter, but it is defined as a type alias that's a member of an abstract class or trait. It can then be made concrete at the moment the abstract definition itself is made concrete. Let's look at the following few lines of code, which will show you how this works:

trait HolderA {
type A
def a: A
}
class A extends HolderA {
override type A = Int
override def a = 10
}

Here, we defined an abstract type member, A, and overrode it in the concrete implementation by binding it to the Int.

It is possible to define multiple type members, of course, and define constraints on them, including type members themselves as part of the constraint:

trait HolderBC {
type B
type C <: B
def b: B
def c: C
}

Type inference is not applied in this case, so the following code will not compile because the type definition is missing:

class BC extends HolderBC {
override def b = "String"
override def c = true
}

These type members can be defined using all language features that can be applied to other type definitions, including multiple type constraints and path-dependent types. In the following example, we demonstrate this by declaring type members in the HolderDEF and providing the concrete definition in the class DEF. Incompatible type definitions are noted as such and commented out:

trait HolderDEF {
type D >: Null <: AnyRef
type E <: AnyVal
type F = this.type
def d: D
def e: E
def f: F
}

class DEF extends HolderDEF {
override type D = String
override type E = Boolean

// incompatible type String
// override type E = String
// override def e = true

override def d = ""
override def e = true

// incompatible type DEF
// override def f: DEF = this

override def f: this.type = this
}

It is also possible to combine type members and type parameters and use them later to further constrain possible definitions of the former:

abstract class HolderGH[G,H] {
type I <: G
type J >: H
def apply(j: J): I
}
class GH extends HolderGH[String, Null] {
override type I = Nothing
override type J = String
override def apply(j: J): I = throw new Exception
}

Type members and type parameters look very similar in their function—this is done to define abstract type definitions that can be refined later. Given this similarity, a developer can use one or another most of the time. Still, there are a couple of nuances regarding the situations in which you should prefer to use them.

These type parameters are usually more straightforward and easier to get right, so generally, they should be preferred. Type members are the way to go if you run into one of the following cases:

  • If the concrete type definition should remain hidden
  • If the intended way to provide the concrete definition of the type is via inheritance (overridden in subclasses or mixed-in via traits)

There is another simple rule that's easy to memorize—type parameters are used to define the types of parameters of the method and type members to define the result type of this method:

trait Rule[In] {
type Out
def method(in: In): Out
}

There is also another way to specify boundaries for type parameters and type members in Scala.

主站蜘蛛池模板: 纳雍县| 噶尔县| 新绛县| 东方市| 海阳市| 肥西县| 翁源县| 义乌市| 平遥县| 横山县| 江安县| 任丘市| 乌兰浩特市| 宁河县| 昌邑市| 赤壁市| 大厂| 高密市| 吉木乃县| 涿州市| 祥云县| 康乐县| 基隆市| 宜兰县| 沾益县| 合作市| 万盛区| 宜宾县| 文登市| 福海县| 合山市| 资兴市| 巴林左旗| 盐津县| 竹北市| 浑源县| 荥阳市| 丽江市| 西畴县| 五华县| 建湖县|