继承和(自动?)类型转换 [英] Inheritance and (automatic?) type conversion

查看:46
本文介绍了继承和(自动?)类型转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请看下面的代码,其中 Extractor[A,B] 是通用框架的一部分,其他所有内容都应视为客户端代码"(我将其简化为一点点并重命名了所有内容.所以不要介意 Extractor 似乎不太有用).

Please have a look at the followin code, where Extractor[A,B] is part of a generic framework and everything else should be regarded as "client code" (I boiled it a down quite a bit and renamed everything. So don't mind that Extractor doesn't seem to be too usefull).

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] 用于从 A 中提取一些值 B 并做一些其他的事情未在此节目代码中表示.抽象类 ValueMyExtractor 用于客户端代码"中类型保存的原因.当我尝试创建 MyExtractorList 时,会发生以下情况:

so in short words Extractor[A,B] is used to extract some value B from A and do some other things that are not represented in this show code. The abstract classes Value and MyExtractor are used for reasons of type savety in the "client code". When I try to create a List of MyExtractors, the following happens:

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)

尝试将 IntExtractor 转换为超类

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

I am aware that everything is fine, when I define IntExtractor like this

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

但我不明白,为什么它不能像我上面尝试的那样工作.如果您提供任何帮助或提示,我将不胜感激.

But I don't understand, why it doesn't work the way I tried it above. I would be thankfull for any help or hints.

推荐答案

据我所知,您正在寻找的概念是协方差".仅仅因为 IntValueValue 的子类型并不意味着 MyExtractor[IntValue]MyExtractor[Value]<的子类型/代码>.默认情况下,这两种类型之间根本没有子类型关系.要创建这样的关系,您需要将 MyExtractor 声明为与其参数相关的协变.Scala 允许您通过在类型参数声明前添加+"来声明类型参数是协变的.这称为方差表示法.

As near as I can tell, the concept you are looking for is "covariance". Just because IntValue is a subtype of Value doesn't mean that MyExtractor[IntValue] is a subtype of MyExtractor[Value]. By default, there is no subtyping relation between those two types at all. To create such a relationship, you need to declare MyExtractor to be covariant with respect to it's parameter. Scala lets you declare type parameters to be covariant by adding a "+" before the type parameters declaration. This is called a variance notation.

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

Scala 还支持类型参数的逆变.逆变就像协方差,但相反,并在类型参数上用-"方差符号表示.你的 Extractor 类型提供了一个很好的例子,说明逆变符号是有意义的.

Scala also supports contravariance over type parameters. Contravariance is just like covariance, but reversed, and is expressed with a "-" variance notation on the type parameter. Your Extractor type provides an excellent example of a place where a contravariance notation makes sense.

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

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

This means that if Foo is a subtype of Bar, then Extractor[Bar, Baz] is a subtype of Extractor[Foo, Baz], which if you think about it makes sense. If something can extract the data you want when passed an instance of a supertype, then by definition it can extract it when passed an instance of a subtype. Conversely, if Foo is a subtype of Bar, then Extractor[Baz, Foo] is a subtype of Extractor[Baz, Bar]. That also makes sense. If you've got an extractor that returns a Foo, you can certainly use it wherever you need an extractor that returns a Bar.

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

There are restrictions on when contravariance and covariance can be declared. For instance, contravariant type parameters can only be used as method arguments, and covariant parameters can only be used as method returns or vals. Neither can be used as vars. It gets more complicated with nested type parameters, but the rules basically boil down to "where it's sensible", and your example meets all of them.

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

Additional side note, all of your abstract classes in your example should probably be declared as traits instead. As long as your abstract classes don't require constructor arguments, declaring them as traits gives you a few more opportunities for reuse.

这篇关于继承和(自动?)类型转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆