Наследование и (автоматическое?) Преобразование типов - PullRequest
7 голосов
/ 12 сентября 2010

Пожалуйста, взгляните на следующий код, где 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 из A и выполнения некоторых других действий, которыене представлены в этом коде шоу.Абстрактные классы Value и MyExtractor используются в целях сохранения типа в «коде клиента».Когда я пытаюсь создать List из MyExtractor s, происходит следующее:

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 Ответ

7 голосов
/ 12 сентября 2010

Насколько я могу судить, концепция, которую вы ищете, это "ковариация". То, что IntValue является подтипом Value, не означает, что MyExtractor[IntValue] является подтипом MyExtractor[Value]. По умолчанию между этими двумя типами вообще нет связи подтипов. Чтобы создать такое отношение, вам нужно объявить MyExtractor ковариантным по отношению к его параметру. Scala позволяет объявлять параметры типа ковариантными, добавляя «+» перед объявлением параметров типа. Это называется обозначением дисперсии.

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

Scala также поддерживает контрастность параметров типа. Контравариантность аналогична ковариантности, но обратная, и выражается с помощью «-» дисперсии параметра типа. Ваш тип Extractor является отличным примером места, где имеет смысл контравариантная запись.

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

Это означает, что если Foo является подтипом Bar, то Extractor[Bar, Baz] является подтипом Extractor[Foo, Baz], что, если подумать, имеет смысл. Если что-то может извлечь нужные данные при передаче экземпляра супертипа, то по определению оно может извлечь его при передаче экземпляра подтипа. И наоборот, если Foo является подтипом Bar, то Extractor[Baz, Foo] является подтипом Extractor[Baz, Bar]. Это также имеет смысл. Если у вас есть экстрактор, который возвращает Foo, вы, безусловно, можете использовать его там, где вам нужен экстрактор, который возвращает Bar.

Существуют ограничения на возможность объявления контравариантности и ковариации. Например, параметры контравариантного типа могут использоваться только в качестве аргументов метода, а ковариантные параметры могут использоваться только в качестве возврата метода или значений. Ни один из них не может быть использован в качестве переменных. С параметрами вложенного типа все усложняется, но правила сводятся к тому, «где это разумно», и ваш пример соответствует всем им.

Дополнительное примечание: все ваши абстрактные классы в вашем примере, скорее всего, должны быть объявлены как черты. Пока ваши абстрактные классы не требуют аргументов конструктора, их объявление в качестве признаков дает вам еще несколько возможностей для повторного использования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...