代码之家  ›  专栏  ›  技术社区  ›  Agl

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

  •  7
  • Agl  · 技术社区  · 14 年前

    请看下面的代码,在哪里 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
    

    简而言之 提取器 用来提取一些价值 B A 并做一些其他的事情,没有在这个显示代码中表示。抽象类 Value MyExtractor 当我试图创造一个 List 属于 我的抽取器

    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
    

    但我不明白,为什么它不能像我上面试过的那样工作。 我会感谢你的帮助和暗示。

    1 回复  |  直到 14 年前
        1
  •  7
  •   Dave Griffith    14 年前

    据我所知,你要找的概念是“协方差”。只是因为 IntValue 是的子类型 Value MyExtractor[IntValue] 是的子类型 MyExtractor[Value] . 默认情况下,这两种类型之间根本没有子类型关系。要创建这样的关系,您需要声明 MyExtractor 关于它的参数是协变的。Scala允许您通过在类型参数声明之前添加“+”来声明协变的类型参数。这称为方差表示法。

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

    Extractor 类型提供了一个很好的例子,说明了逆差表示法是有意义的。

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

    这意味着如果 Foo 是的子类型 Bar Extractor[Bar, Baz] 是的子类型 Extractor[Foo, Baz] 如果你仔细想想,这是有道理的。如果某个对象在传递给某个超类型的实例时可以提取所需的数据,那么根据定义,它在传递给某个子类型的实例时可以提取该数据。相反,如果 是的子类型 酒吧 ,那么 Extractor[Baz, Foo] 是的子类型 Extractor[Baz, Bar] . 这也是有道理的。如果你有一个提取器 ,您当然可以在任何需要返回 酒吧 .

    另外,示例中的所有抽象类都应该声明为traits。只要您的抽象类不需要构造函数参数,将它们声明为traits将为您提供更多的重用机会。