我想知道是否有充分的理由将子类型用作函数类型参数? 让我们考虑以下示例:
scala> trait Animal { def sound: String }
defined trait Animal
scala> def f1[T <: Animal](a: T) = a.sound
f1: [T <: Animal](a: T)String
scala> def f2(a: Animal) = a.sound
f2: (a: Animal)String
f1 与 f2 相比有哪些优势?
答案 0 :(得分:5)
我相信你的例子没有任何优势。类型参数通常用于连接代码的不同部分,但有关T
的信息实际上已丢失,因为它不匹配任何内容。考虑另一个例子:
def f1[T <: Animal](a: T) = (a.sound, a)
def f2(a: Animal) = (a.sound, a)
现在情况有所不同,因为T
被传递给返回类型:
class Dog extends Animal { def sound = "bow-wow" }
f1(new Dog)
//> (String, Dog) = (bow-wow,Dog@141851fd)
f2(new Dog)
//> (String, Animal) = (bow-wow,Dog@5a1fe991)
在这种情况下,您可以将f1
视为&#34;实例化&#34;的模板。在编译时,有效地生成基于编译时参数类型的特定方法。因此,如果您想使用需要f1(new Dog)
的{{1}},则会进行编译,而(String, Dog)
则会赢得
答案 1 :(得分:2)
函数f1
和f2
都非常相似。如果输出字节码,您将在常量池中看到它们被表示为:
#71 = Methodref #12.#70 // Main$.f2:(LMain$Animal;)Ljava/lang/String;
#74 = Methodref #12.#73 // Main$.f1:(LMain$Animal;)Ljava/lang/String;
就字节码而言,它们是将Animal
作为参数并返回String
的函数。
当您想要返回特定的T
(其中T <: Animal
)时,这会变得更有趣。请记住,字节码仍然是相同的,但在编译时,这为T
类型参数提供了更多的含义和功能:
想象一下你有:
def f1[T <: Animal](a: T): T = a // silly, I know
def f2(a: Animal): Animal = a
你试试这个:
val s: Dog = f1(new Dog())
val t: Dog = f2(new Dog()) // NOPE
val u: Dog = f2(new Dog()).asInstanceOf[Dog] // awkward
第二行不会在没有强制转换的情况下编译,这会牺牲编译时类型检查。
答案 2 :(得分:1)
考虑到f1
和f2
具有相同输出类型的示例f2
函数在f1
之前有优势,如果您想重载方法。
因此对于f1
,它会出错:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Zoo {
def f1[T <: Animal](a: T) = a.sound
def f1[T <: Dog](a: T) = "Dog says " + a.sound
}
// Exiting paste mode, now interpreting.
<console>:18: error: method f1 is defined twice
conflicting symbols both originated in file '<console>'
def f1[T <: Dog](a: T) = "Dog says " + a.sound
^
与f2
同时有效:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Zoo {
def f2(a: Animal) = a.sound
def f2(a: Dog) = "Dog says " + a.sound
}
// Exiting paste mode, now interpreting.
defined class Zoo
答案 3 :(得分:0)
在Scala和JVM中,他们有以下函数类型规则
的子类型
S1 -> S2
是T1 -> T2
当且仅当
S1
是T1
的子类型,T2
是S2
的子类型
在您的示例中,
def f1[T <: Animal](a: T): String // T <: Animal -> String
def f2(a: Animal): String // Animal -> String
根据功能输入规则,f1
是f2
总之 f1
和f2
在实际使用案例中没有区别
请参阅以下链接
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Function_types