Чем чаще я пишу на Scala тем больше я понимаю недостатки и преимущества этого языка. Есть ряд не совсем очевидных тонкостей, которые могут усложнить переход на Scala с Java и создать проблемы новичкам.
Дабы упростить жизнь себе и другим на основе своего опыта я записал решения, которые часто применяю на практике.
- Невозможно создать массив на основе generic типа. Допустим есть у нас trait ‘Животное’ (Animal) иtrait ‘Группа животных’ (Animals). Для тех, кто плохо знаком со Scala, стоит отметить, что в данном примере trait’ы играют роль интерфейсов как в Java. У группы животных есть метод same, который принимает массив животных различных видов и возвращает массив таких животных, которые одного вида что и группа. Мне с ходу пришло такое решение:
1234567trait Animaltrait Animals[T <: Animal] {def sames(animals: Array[Animal]): Array[T] = (animals filter (_.isInstanceOf[T]) map (_.asInstanceOf[T]))}
Но, к сожалению, оно не работает. Проблема в методе same. Его реализация возвращает ArraySeq[T] вместо Array[T]. Можно пойти другим способом и реализовать метод same так:
12345678def sames(animals: Array[Animal]): Array[T] = {val filteredCollection = animals filter(_.isInstanceOf[T]) map (_.asInstanceOf[T])val concreteArray = new Array[T](filteredCollection.length)for(i <- 0 to filteredCollection.length - 1) {concreteArray(i) = filteredCollection(i)}concreteArray}
Но и тут код не компилируется: «Cannot find class tag for element type T«. В подробности вдаваться не буду, но суть в том, что создать массив из generic типа мы не можем. А решение проблемы простое. Достаточно добавить «[T: ClassTag]» сразу после названия метода. Тогда и первый и второй варианты будут работать. Ниже представлен полный исправленный первый пример.
123456789import scala.reflect.ClassTagtrait Animaltrait Animals[T <: Animal] {def sames[T: ClassTag](animals: Array[Animal]): Array[T] = animals filter (_.isInstanceOf[T]) map (_.asInstanceOf[T])}
- Рассмотрим пример.
123class Animal(var age: Int)class Dolphin(age: Int) extends Animal(age) { def growUp(years: Int) = age += years }
Есть Дельфин (Dolphin), который унаследован от класса Животное (Animal). У дельфина есть метод ‘подрасти’ (growUp), который принимает кол-во лет на которые нужно подрасти. Это количество лет метод прибавляет к переменной age. С точки зрения Java программиста код должен работать. Но Scala его не компилирует, поскольку считает что age в классе Dolphin — это переопределение. Для работы примера его нужно подправить следующим образом
123class Animal(var age: Int)class Dolphin(tage: Int) extends Animal(tage) { def growUp(years: Int) = age += years } - Продолжение следует…
Описанные тут решения далеко не совершенны, но на практике они проверены и работают. Вполне может быть так, что есть более грамотный подход. Как говорится — «Нет предела совершенству!».