2014-09-29 5 views
0

Я читал через http://oldfashionedsoftware.com/2008/08/26/variance-basics-in-java-and-scala/Scala контравариантен позиция по методу

и смотрю на код

class CoVar[+T](param1: T) { 
    def method1(param2: T) = { } 
    def method2: T = { param1 } 
    def method3: List[T] = { List[T](param1) } 
    def method4[U >: T]: List[U] = { List[U](param1) } 
    val val1: T = method2 
    val val2: Any = param1 
    var var1: T = method2 
    var var2: Any = param1 
} 

тогда, если бы я должен был иметь

val covar1 = new CoVar(new Car) 
val covar2: CoVar[Vehicle] = covar1 //completely legal with covariant 

Теперь, давайте идти через методы

  • meth od1 - Я не понимаю, почему это не компилируется, и это мой главный вопрос.
  • method2 - param1 - это автомобиль и метод2 возвращает транспортное средство, которое отлично, поскольку автомобиль является транспортным средством
  • method3 - since Список [Транспортное средство ] возвращается и автомобилей является Vehicle это прекрасно
  • var1 - тот же самый вопрос я считаю, и не слишком отличается

Я думаю, это было бы нормально с method1 (парам: Vehicle), так как я могу передать в новый автомобиль просто отлично или новый автомобиль просто отлично

но оригинальный класс CoVar do es не компилируется, так как он говорит, что метод1 является контравариантной позицией. Я думал, что контравариантен будет означать, я мог пройти в

Теперь, проходя через это с ContraVar и method1 мы снова

class ContraVar[-T](param1: T) { 
    def method1(param2: T) = { } 
    val val2: Any = param1 
    var var2: Any = param1 
} 

val temp1 = new ContraVar(new Car) 
val temp2: ContraVar[Ford] = temp1 

temp2.method1(new Ford) 
temp2.method1(new FordMustang) 
temp2.method1(new Car) //fails to compile(good) 

которые работают просто отлично. Может кто-нибудь объяснить, почему метод1 ломается на CoVar? Возможно, я возглавляю совершенно неправильный путь на том, что пойдет не так, если бы метод method1 просто компилировался?

спасибо, Dean

ответ

2

Ваш вопрос сводится к тому, почему следующие является незаконным

trait Tool[+A] { 
    def treat(c: A): Unit 
} 

Только представьте, что бы составить ... Давайте посмотрим на прецеденте с внешней стороны:

def apply[A](tool: Tool[A], car: A): Unit = tool.treat(car) 

Скажем, существует два возможных типа для A:

trait Car 
trait Mustang extends Car { def awe(): Unit } 

Ковариация будет означать Tool[Mustang] <: Tool[Car]. Поэтому, когда вас спрашивают Tool[Car], вы можете использовать Tool[Mustang]. Теперь представьте Tool[Mustang]:

val tm = new Tool[Mustang] { def treat(m: Mustang) = m.awe() } 

И теперь вы могли бы назвать:

apply[Car](tm, new Car {}) 

Это означало бы, tm мог получить доступ к несуществующему методу awe в общей машине. Очевидно, что это не отношение типа звука. Следовательно, всякий раз, когда тип используется в позиции аргумента, он должен быть инвариантным или контравариантным.

+0

hmmmm, так что, если бы они решили разрешить только подклассу Инструмент [+ A] и Tool [Mustang] не поддавались подклассу. то есть. есть ли еще один случай, когда метод1 ломается или будет ли один выбор языка помешать этой проблеме? –

+0

Я не понимаю ваш вопрос. Вы можете создать подкласс 'SubTool [A] extends Tool [A]', где вы решите, что 'A' становится инвариантным. Затем вы можете разрешить 'A' появляться в аргументах метода' SubTool'. –

+0

Ох, неважно, я понял ... Я думаю, что лучше добавить «trait SubTool extends Tool [Mustang] {def treat (m: Mustang) = m.awe()} ok, это делает полный смысл для меня сейчас, когда я думаю об этом так, потому что это типичный шаблон java. Другой способ, которым вы его там путали, пока я немного спал над проблемой и не реформировал ее с точки зрения моих знаний о Java. –

1

Давайте вашему method1 тело:

class CoVar[+T] { 
    var listOfT: List[T] = Nil 

    // method1 prepends the given element to listOfT 
    def method1(param2: T) = { listOfT = param2 :: ListOfT } 
} 

Теперь мы на самом деле что-то сделать с параметром, передаваемым в method1, это будет легко увидеть, как что-то не так может произойти, если это было разрешено.

// Lets construct one of these that holds Ints. and add something to it 
val covarInt = new CoVar[Int] 
covarInt.method1(1) 

// lets assign to a more general value, this is no problem because of the covariance 
val covarAny: CoVar[Any] = covarInt 

// now lets do a bad thing: 
covarAny.method1("this is a string not an Int") 

Последняя строка показывает плохую вещь, которая будет разрешена. Так как covarAny имеет тип CoVar[Any], это означает, что в классе CoVar, type T = Any, поэтому тип ожидаемых в качестве входных данных для method1 является Any, так что пропускание String в эту функцию должно быть разрешено, так как String является Any. Однако тело метода затем попытается добавить наш принятый String в список Int s, который не должен быть разрешен.

+0

, что doesn 't выглядит так плохо, так как val myNewList: Любой param2 :: ListOfT действителен, правильно? Я помню, как вы добавляете в свой список, введите вывод, чтобы список стал самым высоким типом, чтобы он выглядел просто отлично и действенно. –

+0

или в scala repl, scala> val myList = 56 :: "hi there" :: Nil myList: Список [Any] = List (56, hi there) действителен. –

+0

да, но listOfT не является типом List [Any] его тип List [T]. подумайте об этом: 'def head: T = listOfT.head' – stew

0

У меня есть новый ответ, который, как я думал, очень помог мне, почему параметры являются контравариантными, а не ковариантными. Этот пример можно сделать без генераторов.

Давайте использовать функциональные объекты, а и определить животных, птиц, и класс Duck

class Animal { 
    def makeSound() = "animalsound" 
    def walk() 
} 
class Bird extends Animal { 
    def makeSound() = "tweeeeet" 
    def fly() 
} 
class Duck extends Bird { 
    def makeSound() = "quack" 
    def paddle() 
} 

Теперь давайте попробуем определить Ковариантный функцию

val doSomething: (Bird => String) = { d:Duck => d.paddle() } 

Естественно, когда птица передается в, мы не можем бросить утку, так как это может быть не Утка, и она, конечно же, не может грести без весла в классе птиц.

Теперь давайте попробуем определить Контравариантный функцию

val doSomething: (Bird => String) = { a:Animal => a.walk() } 

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

Теперь, есть ли еще один способ, мы могли бы сделать что-то здесь, как в T>: Возможно, Птица? Интересно, должен ли я сделать (T>: Bird => String) = .....

ну, я думаю, что я не знаю, что есть в этом пункте, и оставляю это как TODO в моем списке вещи, о которых нужно узнать.