Мне понравился ответ Роланда Эвальда, так как он описал с очень простым вариантом использования псевдонима типа и для более подробной информации представил очень хороший учебник. Однако, поскольку в этом посте представлен еще один вариант использования с именами членов типа , я хотел бы упомянуть наиболее практичный вариант его использования, который мне очень понравился: (эта часть взята отсюда :)
Абстрактный тип:
type T
T выше говорит, что этот тип, который будет использоваться, пока неизвестен, и в зависимости от конкретного подкласса он будет определен. Лучший способ всегда понять концепции программирования - привести пример. Предположим, у вас есть следующий сценарий:
Здесь вы получите ошибку компиляции, потому что метод eat в классах Cow и Tiger не переопределяет метод eat в классе Animal, потому что их типы параметров различны. Это Трава в классе Корова и Мясо в классе Tiger vs. Food в классе Animal, который является суперклассом, и все подклассы должны соответствовать.
Теперь вернемся к абстракции типов, используя следующую диаграмму и просто добавив абстракцию типов, вы можете определить тип ввода в соответствии с самим подклассом.
Теперь посмотрите на следующие коды:
val cow1: Cow = new Cow
val cow2: Cow = new Cow
cow1 eat new cow1.SuitableFood
cow2 eat new cow1.SuitableFood
val tiger: Tiger = new Tiger
cow1 eat new tiger.SuitableFood // Compiler error
Компилятор доволен, и мы улучшаем наш дизайн. Мы можем кормить нашу корову коровой. Подходящие корма и компилятор мешают нам кормить коров едой, подходящей для тигра. Но что, если мы хотим сделать различие между типом коровы1 Подходящая еда и корова2 Подходящая еда? Другими словами, в некоторых сценариях было бы очень удобно, если бы путь, по которому мы достигаем тип (конечно, через объект), действительно имел значение. Благодаря расширенным функциям в Scala возможно:
Типы, зависящие от пути:
объекты Scala могут иметь типы в качестве членов. Значение типа зависит от пути, который вы используете для доступа к нему. Путь определяется ссылкой на объект (он же экземпляр класса). Для реализации этого сценария необходимо определить класс Grass внутри Cow, т. Е. Cow - это внешний класс, а Grass - внутренний класс. Структура будет такой:
class Cow extends Animal {
class Grass extends Food
type SuitableFood = Grass
override def eat(food: this.SuitableFood): Unit = {}
}
class Tiger extends Animal {
class Meat extends Food
type SuitableFood = Meat
override def eat(food: this.SuitableFood): Unit = {}
}
Теперь, если вы попытаетесь скомпилировать этот код:
1. val cow1: Cow = new Cow
2. val cow2: Cow = new Cow
3. cow1 eat new cow1.SuitableFood
4. cow2 eat new cow1.SuitableFood // compilation error
В строке 4 вы увидите ошибку, поскольку Grass теперь является внутренним классом Cow, поэтому для создания экземпляра Grass нам нужен объект cow, и этот объект cow определяет путь. Таким образом, 2 объекта коровы дают начало двум различным путям. В этом сценарии cow2 хочет есть только пищу, специально созданную для этого. Так:
cow2 eat new cow2.SuitableFood
Теперь все счастливы :-)