继承和(自动?)类型转换

2024-03-23

请看一下下面的代码,其中Extractor[A,B]是通用框架的一部分,其他所有内容都应该被视为“客户端代码”(我对它进行了相当多的简化并重命名了所有内容。所以不要介意Extractor好像不太有用)。

scala> abstract class Extractor[A,B] {                                          
     |   def extract(d:A):B                                                     
     |   def stringRepr(d:A):String                                             
     | }                                                                        
defined class Extractor

scala> sealed abstract class Value                                              
defined class Value

scala> case class IntValue(i:Int) extends Value                                 
defined class IntValue

scala> case class StringValue(s:String) extends Value                           
defined class StringValue

scala> case class Data(i:Int, s:String)                                         
defined class Data

scala> sealed abstract class MyExtractor[Value] extends Extractor[Data, Value] {
     |   def stringRepr(d:Data) = extract(d) match {                            
     |     case IntValue(i) => i.toString                                       
     |     case StringValue(s) => s                                             
     |   }                                                                      
     | }                                                                        
defined class MyExtractor

scala> class IntExtractor(name:String) extends MyExtractor[IntValue] {
     |   def extract(d:Data) = IntValue(d.i)
     | }
defined class IntExtractor

scala> class StringExtractor(name:String) extends MyExtractor[StringValue] {
     |   def extract(d:Data) = StringValue(d.s)
     | }
defined class StringExtractor

简而言之Extractor[A,B]用于提取一些价值B from A并执行此显示代码中未表示的其他一些操作。抽象类Value and MyExtractor出于“客户端代码”中类型安全的原因而使用。 当我尝试创建一个List of MyExtractor即,会发生以下情况:

scala> val l = List.empty[MyExtractor[Value]]
l: List[MyExtractor[Value]] = List()

scala> new IntExtractor("test1") :: l
res5: List[MyExtractor[_ >: IntValue <: Value]] = List(IntExtractor@1fd96c5)

试图转换一个IntExractor到超类

scala> new IntExtractor("test"):MyExtractor[Value]   
<console>:24: error: type mismatch;
 found   : IntExtractor
 required: MyExtractor[Value]
       new IntExtractor("test"):MyExtractor[Value]
       ^

scala> new IntExtractor("test"):Extractor[Data,Value]
<console>:24: error: type mismatch;
 found   : IntExtractor
 required: Extractor[Data,Value]
       new IntExtractor("test"):Extractor[Data,Value]

当我定义时,我知道一切都很好IntExtractor像这样

scala> class IntExtractor(name:String) extends MyExtractor[Value] {
     |   def extract(d:Data) = IntValue(d.i)                            
     | }
defined class IntExtractor

scala> new IntExtractor("test"):Extractor[Data,Value]              
res17: Extractor[Data,Value] = IntExtractor@1653d7a

但我不明白,为什么它按照我上面尝试的方式不起作用。 我将不胜感激任何帮助或提示。


据我所知,您正在寻找的概念是“协方差”。只是因为IntValue是一个子类型Value并不意味着MyExtractor[IntValue]是一个子类型MyExtractor[Value]。默认情况下,这两种类型之间根本不存在子类型关系。要创建这样的关系,您需要声明MyExtractor就其参数而言是协变的。 Scala 允许您通过在类型参数声明之前添加“+”来将类型参数声明为协变的。这称为方差表示法。

sealed abstract class MyExtractor[+Value] extends Extractor[Data, Value] {        
}   

Scala 还支持类型参数的逆变。逆变与协方差类似,但相反,并在类型参数上用“-”方差表示法表示。你的Extractortype 提供了一个很好的例子,说明逆变符号有意义。

abstract class Extractor[-A,+B] {                                          
   def extract(d:A):B                                                     
   def stringRepr(d:A):String                                             
}       

这意味着如果Foo是一个子类型Bar, then Extractor[Bar, Baz]是一个子类型Extractor[Foo, Baz],如果你仔细想想,这是有道理的。如果某些东西可以在传递超类型的实例时提取您想要的数据,那么根据定义,它可以在传递子类型的实例时提取它。相反,如果Foo是一个子类型Bar, then Extractor[Baz, Foo]是一个子类型Extractor[Baz, Bar]。这也是有道理的。如果你有一个返回 a 的提取器Foo,您当然可以在任何需要返回返回值的提取器的地方使用它Bar.

何时可以声明逆变和协变是有限制的。例如,逆变类型参数只能用作方法参数,而协变参数只能用作方法返回值或值。两者都不能用作变量。嵌套类型参数会变得更加复杂,但规则基本上可以归结为“合理的地方”,并且您的示例满足所有这些规则。

附加说明,示例中的所有抽象类可能都应该声明为特征。只要您的抽象类不需要构造函数参数,将它们声明为特征就可以为您提供更多重用机会。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

继承和(自动?)类型转换 的相关文章

随机推荐