Scala 上限
Scala upper bounds
在典型的 Scala 上限示例中
abstract class Animal {
def name: String
}
abstract class Pet extends Animal {}
class Cat extends Pet {
override def name: String = "Cat"
}
class Dog extends Pet {
override def name: String = "Dog"
}
class Lion extends Animal {
override def name: String = "Lion"
}
这有什么区别
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
val dogContainer = new PetContainer[Dog](new Dog)
val catContainer = new PetContainer[Cat](new Cat)
还有这个?
class PetContainer1(p: Pet) {
def pet: Pet = p
}
val dogContainer1 = new PetContainer1(new Dog)
val catContainer1 = new PetContainer1(new Cat)
使用类型上限与直接使用抽象 class/trait 相比有什么优势?
使用上限,您可以拥有特定子类型的集合 - 因此仅限于猫或狗,您可以从 def pet
返回特定子类型。 PetContainer1
.
不正确
丢失更准确的类型信息示例:
val doggo: Dog = new Dog
val dogContainer1 = new PetContainer1(doggo)
// the following won't compile
// val getDoggoBack: Dog = dogContainer1.pet
val dogContainer2 = new PetContainer[Dog](doggo)
// that works
val getDoggoBack: Dog = dogContainer2.pet
您不能将猫放入狗容器中:
// this will not compile
new PetContainer[Dog](new Cat)
如果你要处理多个元素的集合,它会变得更重要。
两者的主要区别其实就是这两种类型的区别:
def pet: P = p
// and
def pet: Pet = p
所以,在第一个例子中,pet
的类型是P
,而在第二个例子中,pet
的类型是Pet
。换句话说:在第一个示例中,类型 更精确 ,因为它是 Pet
.
的特定子类型
如果我在 PetContainer[Dog]
中放入 Dog
,我会得到一个 Dog
。然而,如果我在 PetContainer1
中放置一个 Dog
,我得到一个 Pet
,它可以是 Cat
或 Dog
,或者其他完全。 (您还没有创建 类 sealed
或 final
,因此其他人可以一起创建他们自己的 Pet
子类。)
如果您使用显示表达式类型的编辑器或 IDE,或者您在 REPL 或 Scastie 中尝试您的代码,您实际上会看到不同之处:
dogContainer.pet // has type `Dog`
catContainer.pet // has type `Cat`
dogContainer1.pet // has type `Pet`
catContainer1.pet // has type `Pet`
请参阅 Scastie link 示例:
sealed trait Animal:
val name: String
sealed trait Pet extends Animal
case object Cat extends Pet:
override val name = "Cat"
case object Dog extends Pet:
override val name = "Dog"
case object Lion extends Animal:
override val name = "Lion"
final case class PetContainer[+P <: Pet](pet: P)
val dogContainer = PetContainer(Dog)
val catContainer = PetContainer(Cat)
final case class PetContainer1(pet: Pet)
val dogContainer1 = PetContainer1(Dog)
val catContainer1 = PetContainer1(Cat)
dogContainer.pet //=> Dog: Dog
catContainer.pet //=> Cat: Cat
dogContainer1.pet //=> Dog: Pet
catContainer1.pet //=> Cat: Pet
如果你有一个可以容纳两只宠物的狗舍,这会变得更有趣:
sealed trait Pet:
val name: String
case object Cat extends Pet:
override val name = "Cat"
case object Mouse extends Pet:
override val name = "Mouse"
final case class UnsafePetKennel(pet1: Pet, pet2: Pet)
val unsafeCatKennel = UnsafePetKennel(Cat, Cat)
val unsafeMouseKennel = UnsafePetKennel(Mouse, Mouse)
val oopsSomeoneAteMyMouse = UnsafePetKennel(Cat, Mouse)
final case class SafePetKennel[+P <: Pet](pet1: P, pet2: P)
val safeCatKennel = SafePetKennel[Cat.type](Cat, Cat)
val safeMouseKennel = SafePetKennel[Mouse.type](Mouse, Mouse)
val mouseIsSafe = SafePetKennel[Cat.type](Cat, Mouse)
// Type error: found `Mouse`, required `Cat`
val mouseStillSafe = SafePetKennel[Mouse.type](Cat, Mouse)
// Type error: found `Cat`, required `Mouse`
不幸的是,如果我们依赖 Scala 的类型参数推断,事情仍然会出错:
val inferredKennel = SafePetKennel(Cat, Mouse)
有效,因为 Scala 推断 P
是 Cat
和 Mouse
的最小上限,即 Pet
,因此 inferredKennel
有键入 SafePetKennel[Pet]
,因为 Cat
和 Mouse
都是 Pet
的子类型,所以这是允许的。
我们可以通过对 SafePetKennel
:
稍作修改来解决这个问题
final case class SaferKennel[+P <: Pet, +Q <: Pet](
pet1: P, pet2: Q)(implicit ev: P =:= Q)
val inferredKennel = SaferKennel(Cat, Mouse)
// Cannot prove that Cat =:= Mouse.
在典型的 Scala 上限示例中
abstract class Animal {
def name: String
}
abstract class Pet extends Animal {}
class Cat extends Pet {
override def name: String = "Cat"
}
class Dog extends Pet {
override def name: String = "Dog"
}
class Lion extends Animal {
override def name: String = "Lion"
}
这有什么区别
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
val dogContainer = new PetContainer[Dog](new Dog)
val catContainer = new PetContainer[Cat](new Cat)
还有这个?
class PetContainer1(p: Pet) {
def pet: Pet = p
}
val dogContainer1 = new PetContainer1(new Dog)
val catContainer1 = new PetContainer1(new Cat)
使用类型上限与直接使用抽象 class/trait 相比有什么优势?
使用上限,您可以拥有特定子类型的集合 - 因此仅限于猫或狗,您可以从 def pet
返回特定子类型。 PetContainer1
.
丢失更准确的类型信息示例:
val doggo: Dog = new Dog
val dogContainer1 = new PetContainer1(doggo)
// the following won't compile
// val getDoggoBack: Dog = dogContainer1.pet
val dogContainer2 = new PetContainer[Dog](doggo)
// that works
val getDoggoBack: Dog = dogContainer2.pet
您不能将猫放入狗容器中:
// this will not compile
new PetContainer[Dog](new Cat)
如果你要处理多个元素的集合,它会变得更重要。
两者的主要区别其实就是这两种类型的区别:
def pet: P = p
// and
def pet: Pet = p
所以,在第一个例子中,pet
的类型是P
,而在第二个例子中,pet
的类型是Pet
。换句话说:在第一个示例中,类型 更精确 ,因为它是 Pet
.
如果我在 PetContainer[Dog]
中放入 Dog
,我会得到一个 Dog
。然而,如果我在 PetContainer1
中放置一个 Dog
,我得到一个 Pet
,它可以是 Cat
或 Dog
,或者其他完全。 (您还没有创建 类 sealed
或 final
,因此其他人可以一起创建他们自己的 Pet
子类。)
如果您使用显示表达式类型的编辑器或 IDE,或者您在 REPL 或 Scastie 中尝试您的代码,您实际上会看到不同之处:
dogContainer.pet // has type `Dog`
catContainer.pet // has type `Cat`
dogContainer1.pet // has type `Pet`
catContainer1.pet // has type `Pet`
请参阅 Scastie link 示例:
sealed trait Animal:
val name: String
sealed trait Pet extends Animal
case object Cat extends Pet:
override val name = "Cat"
case object Dog extends Pet:
override val name = "Dog"
case object Lion extends Animal:
override val name = "Lion"
final case class PetContainer[+P <: Pet](pet: P)
val dogContainer = PetContainer(Dog)
val catContainer = PetContainer(Cat)
final case class PetContainer1(pet: Pet)
val dogContainer1 = PetContainer1(Dog)
val catContainer1 = PetContainer1(Cat)
dogContainer.pet //=> Dog: Dog
catContainer.pet //=> Cat: Cat
dogContainer1.pet //=> Dog: Pet
catContainer1.pet //=> Cat: Pet
如果你有一个可以容纳两只宠物的狗舍,这会变得更有趣:
sealed trait Pet:
val name: String
case object Cat extends Pet:
override val name = "Cat"
case object Mouse extends Pet:
override val name = "Mouse"
final case class UnsafePetKennel(pet1: Pet, pet2: Pet)
val unsafeCatKennel = UnsafePetKennel(Cat, Cat)
val unsafeMouseKennel = UnsafePetKennel(Mouse, Mouse)
val oopsSomeoneAteMyMouse = UnsafePetKennel(Cat, Mouse)
final case class SafePetKennel[+P <: Pet](pet1: P, pet2: P)
val safeCatKennel = SafePetKennel[Cat.type](Cat, Cat)
val safeMouseKennel = SafePetKennel[Mouse.type](Mouse, Mouse)
val mouseIsSafe = SafePetKennel[Cat.type](Cat, Mouse)
// Type error: found `Mouse`, required `Cat`
val mouseStillSafe = SafePetKennel[Mouse.type](Cat, Mouse)
// Type error: found `Cat`, required `Mouse`
不幸的是,如果我们依赖 Scala 的类型参数推断,事情仍然会出错:
val inferredKennel = SafePetKennel(Cat, Mouse)
有效,因为 Scala 推断 P
是 Cat
和 Mouse
的最小上限,即 Pet
,因此 inferredKennel
有键入 SafePetKennel[Pet]
,因为 Cat
和 Mouse
都是 Pet
的子类型,所以这是允许的。
我们可以通过对 SafePetKennel
:
final case class SaferKennel[+P <: Pet, +Q <: Pet](
pet1: P, pet2: Q)(implicit ev: P =:= Q)
val inferredKennel = SaferKennel(Cat, Mouse)
// Cannot prove that Cat =:= Mouse.