2012-01-18 2 views
3

Я часто нажимаю пределы системы типа Java через мое использование Guice, TypeLiteral, дженериков и подстановочных знаков. Я часто сталкиваюсь с ситуациями, когда мне нужно выполнять непроверенные броски, что в значительной степени разрушает безопасность типов - другими словами, «Древовидный ад».Какие механизмы имеет Scala для дженериков и подстановочных знаков по сравнению с Java?

Вот упрощенный пример некоторых проблемных Java-кодов.

class SquareDrawer implements ShapeDrawer<Row<Square>> {} 
class Client { 
    Key<SquareDrawer> SQUARE_DRAWER_KEY = 
     Key.get(SquareDrawer.class, randomAnnotation()); 
    void bindShapeDrawer(
     Key<? extends ShapeDrawer<Row<? extends Shape>>> shapeDrawer) {} 

    Client() { 
     // Note Unchecked cast required below 
     bindShapeDrawer( 
     (Key<? extends ShapeDrawer<Row<? extends Shape>>>) SQUARE_DRAWER_KEY); 
    } 
} 

Я изучаю Scala и были под впечатлением (или иллюзии), что он имеет лучшую поддержку дженериков, чем Java. Может ли вышеуказанный код быть написан в Scala, чтобы избежать непроверенных бросков?

Есть еще необходимость в Guice's TypeLiteral в Scala?

ответ

2

Scala имеет форму reified types под названием «Манифесты». Они позволяют делать вещи, которые были бы довольно неуклюжими на Java из-за стирания типа. Читайте все о них здесь: http://www.scala-blogs.org/2008/10/manifests-reified-types.html

+0

За исключением того, что Манифесты не являются овериями. Они просто сообщают компилятору, что ему не нужно жаловаться на некоторые виды использования дженериков (например, динамически их создавать), показывая компилятору все разные пути кода, которые действительно могут быть приняты в рамках проявленного общего. – Destin

+1

@Destin: Не совсем ... – soc

+0

@soc http://stackoverflow.com/questions/3587286/how-does-scalas-2-8-manifest-work – Destin

2

Следующий (надеюсь) эквивалентный код Scala компилируется без ошибок. Возможно, мне нужно указать, что он не содержит динамических бросков. Обратите внимание, что мне приходилось делать Key ковариант в аргументе своего типа, потому что SquareDrawer является только подтипом ShapeDrawer[Row[Square]].

trait ShapeDrawer[A] 
trait Row[A] 
trait Shape 
trait Square extends Shape 
trait Key[+A] 

//your code starts here 
trait SquareDrawer extends ShapeDrawer[Row[Square]] 

class Client{ 
    val SDK = new Key[SquareDrawer]{} 

    bindShapeDrawer(SDK) 

    def bindShapeDrawer[SD[A] <: ShapeDrawer[A],S <: Shape](shapeDrawer: Key[SD[Row[S]]]) {} 
} 
+0

Спасибо! Таким образом, если бы можно было вызвать 'bindShapeDrawer (новый ключ [ProgramDrawer] {})' где 'ProgramDrawer' определяется как' trait ProgramDrawer расширяет ShapeDrawer [Row [Program]] и 'Program' определяется как' trait Program', Scala правильно сообщает ошибку типа компиляции? –

+0

@glenviewjeff Поскольку 'Программа' не является' Shape', конечно. – ziggystar

3

Есть несколько вещей, которые предлагает Scala.

  • Высшее kinded типа (я надеюсь, что я использую термин правильно) позволяют определить такие вещи, как «любого типа, имеющей другой тип в качестве параметра типа» AFAIK нет никакого способа, чтобы выразить, что в Java

  • Параметры Co и контравариантного типа. В java вы можете делать параметры один или другой, используя подстановочные знаки в каждом месте, где они используются. В Scala вы просто объявляете их как таковые.

  • Тип свидетелей (Опять же: это правильный термин?) Являются неявными функциями, которые демонстрируют некоторое свойство аргументов типа, тем самым определяя ограничения на тип. Если существует неявное преобразование, соответствующее объявлению свидетеля, вызов будет скомпилировать условие.

  • Путь зависимых типов. У вас могут быть типы, которые являются элементами экземпляров, поэтому каждый экземпляр имеет свой собственный тип. Снова вы не можете сделать это в java afaik.

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