- Learn Scala Programming
- Slava Schmidt
- 463字
- 2021-06-10 19:35:45
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.
- Oracle從新手到高手
- Microsoft Application Virtualization Cookbook
- Animate CC二維動畫設計與制作(微課版)
- INSTANT Sencha Touch
- Julia高性能科學計算(第2版)
- Android系統原理及開發要點詳解
- 區塊鏈技術進階與實戰(第2版)
- 一本書講透Java線程:原理與實踐
- Java 9 Programming By Example
- PHP與MySQL權威指南
- PHP+MySQL動態網站開發從入門到精通(視頻教學版)
- 邊玩邊學Scratch3.0少兒趣味編程
- HTML5 Canvas核心技術:圖形、動畫與游戲開發
- Java EE基礎實用教程
- 循序漸進Vue.js 3前端開發實戰