2010-09-12 2 views
7

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

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

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

пытается преобразовать 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] = [email protected] 

Но я не понимаю, почему это не работает так, как я попробовал это выше. Я был бы благодарен за любую помощь или подсказки.

+6

Как правило, полезно использовать имена фактических классов для параметров типа. Например, в 'class MyExtractor [Value]' 'Value' является параметром типа и не имеет ничего общего с вашим значением класса. Это использование имен, как правило, путается с людьми, хотя, конечно, это никогда не ослабит компилятор. –

+0

Ваш 'Extractor [A, B]' ведет себя как 'A => B' (т. Е.' Function1'). Обратите внимание, что 'Function1' является ковариантным в своем возвращаемом типе - он объявлен как' Function1 [-A, + B] ' –

+0

@ Randall Schulz: я не 100% shure Я понимаю вас прав, но класс' Value' в 'классе MyExtractor [Value] 'Соответствует« Value класса ». Например, я проверяю только подклассы 'Value' в' stringRepr (d: Value): String' – Agl

ответ

7

Насколько я могу судить, концепция, которую вы ищете, является «ковариацией». То, что 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.

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

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

+0

Очень хорошее объяснение! – Landei

+0

ничего себе! спасибо за ваш ответ Дэйв. Короче говоря: все изменилось, как вы сказали, и оно работает. Спасибо! Чтобы сделать это немного lnger: теперь, когда вы указали это, это действительно очевидно (даже ограничения), хотя я все еще не могу обойти эту проблему контравариантности. , , все еще есть кое-что, что нужно сделать;). Я использовал абстрактные классы, так как я не знал, что черты могут иметь модификатор «запечатан». Но затем снова . , , почему нет? ;) Большое спасибо – Agl

Смежные вопросы