Scala: примеры классов, сопоставление с образцом, Option, частичные функции, дженерики
Цели курса
- Освойте использование примеров классов
- Освойте использование сопоставления с образцом
1. Образец класса
Образец класса — это специальный класс, который можно использовать для быстрого определениясохранять данныеКласс (похожий на класс Java POJO), который будет часто использоваться при последующем изучении параллельного программирования и фреймворков, таких как spark и flink.
1.1 Определите класс образца
формат синтаксиса
case class 样例类名([var/val] 成员变量名1:类型1, 成员变量名2:类型2, 成员变量名3:类型3)
- Если вы хотите реализовать переменную-член, которую можно изменить, вы можете добавить var
- По умолчанию val, можно не указывать
1.2 Определите примерный класс
необходимость
- Определите класс примера Person с переменными-членами name и age
- Создайте экземпляр класса образца ("Zhangsan", 20) и распечатайте его.
Код ссылки
object _01CaseClassDemo {
case class Person(name:String, age:Int)
def main(args: Array[String]): Unit = {
val zhangsan = Person("张三", 20)
println(zhangsan)
}
}
1.3 Изменяемые переменные-члены
необходимость
- Определите класс примера Person с переменными-членами name и age
- Создайте экземпляр объекта примера класса («Чжан Сан», 20)
- Измените возраст Чжан Саня на 23 года., и распечатать
Код ссылки
object _02CaseClassDemo {
case class Person(var name:String, var age:Int)
def main(args: Array[String]): Unit = {
val zhangsan = Person("张三", 20)
zhangsan.age = 23
println(zhangsan)
}
}
1.4 Методы демонстрационного класса
Когда мы определяем пример класса, компилятор автоматически помогает нам реализовать следующие полезные методы:
- применить метод
- метод toString
- метод равенства
- метод hashCode
- метод копирования
1.4.1 Метод применения
Метод применения позволяет нам быстро создать объект, используя имя класса. См. следующий код:
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
}
}
1.4.2 Метод toString
toString возвращает имя класса образца (переменная-член 1, переменная-член 2, переменная-член 3....), мы можем просмотреть членов класса-образца в других аспектах.
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
// 输出:CasePerson(李四,21)
}
}
1.4.3 метод равенства
Образец класса автоматически реализует метод equals, и вы можете напрямую использовать ==, чтобы сравнить, равны ли два образца классов, то есть равны ли все переменные-члены.
Пример
- Создайте образец класса Person с именем, возрастом
- Создайте два объекта с именем «Ли Си» и возрастом 21 год.
- сравнить их на равенство
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 21)
println(lisi1 == lisi2)
// 输出:true
1.4.4 метод hashCode
Пример класса автоматически реализует метод hashCode. Если значения всех переменных-членов одинаковы, значение хеш-функции одинаковое. Пока одно отличается, значение хеш-функции другое.
Пример
- Создавайте объекты с именами «Ли Си» и возрастом 21 год.
- Создайте другое имя и возраст соответственно "Ли Си",22Объект
- Распечатайте хэш-значения этих двух объектов отдельно
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 22)
println(lisi1.hashCode())
println(lisi2.hashCode())
1.4.5 метод копирования
Пример класса реализует метод копирования, который может быстро создать идентичный экземпляр объекта и может использовать именованные параметры для назначения членам для повторного назначения.
Пример
- Создавайте объекты с именами «Ли Си» и возрастом 21 год.
- Копировать за копией, объект под названием "Ван Ву"
val lisi1 = CasePerson("李四", 21)
val wangwu = lisi1.copy(name="王五")
println(wangwu)
2. Образец объекта
В основном используется в двух местах:
- определить перечисление
- Передано как сообщение без каких-либо параметров (программирование Akka будет рассмотрено позже)
2.1 Определения
Используйте объекты case для создания примеров объектов. Образец объекта является синглтоном, и оннет основного конструктора
формат синтаксиса
case object 样例对象名
3. Сопоставление с образцом
В scala есть очень мощный механизм сопоставления с образцом, который можно применять во многих сценариях:
- оператор переключения
- Введите запрос
- Быстрое получение данных с помощью сопоставления с образцом
3.1 Простое сопоставление с образцом
В Java есть ключевое слово switch, которое может упростить оператор условного суждения if. В scala вместо этого можно использовать выражения соответствия.
формат синтаксиса
变量 match {
case "常量1" => 表达式1
case "常量2" => 表达式2
case "常量3" => 表达式3
case _ => 表达式4 // 默认配
}
Пример
Заявление о потребностях
- Введите слово из консоли (используя метод StdIn.readLine)
- Определите, может ли слово соответствовать следующим словам, если оно может соответствовать, верните предложение
- напечатать это предложение
слово | возвращение |
---|---|
hadoop | Платформа распределенного хранения и вычислений больших данных |
zookeeper | Структура службы распределенной координации больших данных |
spark | Платформа вычислений с распределенной памятью для больших данных |
не соответствует | не соответствует |
Код ссылки
println("请输出一个词:")
// StdIn.readLine表示从控制台读取一行文本
val name = StdIn.readLine()
val result = name match {
case "hadoop" => "大数据分布式存储和计算框架"
case "zookeeper" => "大数据分布式协调服务框架"
case "spark" => "大数据分布式内存计算框架"
case _ => "未匹配"
}
println(result)
3.2 Тип соответствия
Помимо сопоставления данных, таких как switch в Java, выражения сопоставления также могут выполнять сопоставление типов. Если мы хотим выполнять разную логику в соответствии с разными типами данных, мы также можем использовать выражения соответствия для достижения.
определение
формат синтаксиса
变量 match {
case 类型1变量名: 类型1 => 表达式1
case 类型2变量名: 类型2 => 表达式2
case 类型3变量名: 类型3 => 表达式3
...
case _ => 表达式4
}
Пример
Заявление о потребностях
- Определите переменную как тип Any, а затем назначьте ее как «hadoop», 1, 1.0 соответственно.
- Определите сопоставление с образцом, а затем напечатайте имя типа отдельно
Код ссылки
val a:Any = "hadoop"
val result = a match {
case _:String => "String"
case _:Int => "Int"
case _:Double => "Double"
}
println(result)
[!NOTE]
Если вам не нужно использовать совпадающую переменную в выражении case, вместо этого вы можете использовать символ подчеркивания.
3.3 Охранник
В Java вы можете просто добавить несколько меток case.Например, чтобы сопоставить 0-7, вам нужно написать 8 операторов case. Например:
int a = 0;
switch(a) {
case 0: a += 1;
case 1: a += 1;
case 2: a += 1;
case 3: a += 1;
case 4: a += 2;
case 5: a += 2;
case 6: a += 2;
case 7: a += 2;
default: a = 0;
}
В scala охранники можно использовать для упрощения приведенного выше кода, т. е. вДобавьте условие if в оператор case.
Пример
Заявление о потребностях
- Прочитайте число a из консоли (используя StdIn.readInt)
- Если a >= 0 и a
- Если a >= 4 и a
- В противном случае выведите несоответствие
Код ссылки
val a = StdIn.readInt()
a match {
case _ if a >= 0 && a <= 3 => println("[0-3]")
case _ if a >= 4 && a <= 8 => println("[3-8]")
case _ => println("未匹配")
}
3.4 Сопоставление типовых классов
Scala может использовать сопоставление с образцом для сопоставления с образцом класса, чтобы можно было быстро получить данные членов в образце класса. Позже мы будем использовать его при разработке кейсов Akka.
Пример
Заявление о потребностях
- Создайте два примера классов Customer, Order
- Клиент содержит поля имени, возраста
- Заказ содержит поле id
- Определите объекты двух классов case соответственно и укажите их как тип Any
- Используйте шаблон, чтобы сопоставить эти два объекта и вывести значения их переменных-членов соответственно.
Код ссылки
// 1. 创建两个样例类
case class Person(name:String, age:Int)
case class Order(id:String)
def main(args: Array[String]): Unit = {
// 2. 创建样例类对象,并赋值为Any类型
val zhangsan:Any = Person("张三", 20)
val order1:Any = Order("001")
// 3. 使用match...case表达式来进行模式匹配
// 获取样例类中成员变量
order1 match {
case Person(name, age) => println(s"姓名:${name} 年龄:${age}")
case Order(id1) => println(s"ID为:${id1}")
case _ => println("未匹配")
}
}
3.5 Соответствующие наборы
Сопоставление с образцом в scala также можно использовать для сопоставления коллекций.
3.6 Сопоставление массивов
Пример описания
-
Измените код по очереди, чтобы определить следующие три массива.
Array(1,x,y) // 以1开头,后续的两个元素不固定 Array(0) // 只匹配一个0元素的元素 Array(0, ...) // 可以任意数量,但是以0开头
-
Используйте шаблон, чтобы соответствовать приведенному выше массиву
Код ссылки
val arr = Array(1, 3, 5)
arr match {
case Array(1, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
case Array(0, _*) => println("0 ...")
case _ => println("something else")
}
3.7 Список матчей
Пример описания
-
Измените код по очереди, чтобы определить следующие три списка.
List(0) // 只保存0一个元素的列表 List(0,...) // 以0开头的列表,数量不固定 List(x,y) // 只包含两个元素的列表
-
Используйте шаблон, чтобы соответствовать приведенному выше списку
Код ссылки
val list = List(0, 1, 2)
list match {
case 0 :: Nil => println("只有0的列表")
case 0 :: tail => println("0开头的列表")
case x :: y :: Nil => println(s"只有另两个元素${x}, ${y}的列表")
case _ => println("未匹配")
}
3.8 Сопоставление кортежей
Пример описания
-
Измените код по очереди, чтобы определить следующие два кортежа.
(1, x, y) // 以1开头的、一共三个元素的元组 (x, y, 5) // 一共有三个元素,最后一个元素为5的元组
-
Используйте шаблон, чтобы соответствовать вышеуказанным элементам
Код ссылки
val tuple = (2, 2, 5)
tuple match {
case (1, x, y) => println(s"三个元素,1开头的元组:1, ${x}, ${y}")
case (x, y, 5) => println(s"三个元素,5结尾的元组:${x}, ${y}, 5")
case _ => println("未匹配")
}
3.9 Сопоставление с образцом в объявлениях переменных
При определении переменных можно использовать сопоставление с образцом для быстрого получения данных.
3.9.1 Пример | Получение элементов массива
Заявление о потребностях
- Создайте массив, содержащий числа 0-10, используйте сопоставление с образцом, чтобы получить второй, третий и четвертый элементы соответственно.
Код ссылки
val array = (1 to 10).toArray
val Array(_, x, y, z, _*) = array
println(x, y, z)
3.9.2 Пример | Получение данных в списке
Заявление о потребностях
- Создайте список, содержащий числа 0-10, используйте сопоставление с образцом, чтобы получить первый и второй элементы соответственно.
Код ссылки
val list = (1 to 10).toList
val x :: y :: tail = list
println(x, y)
4. Тип опции
Использование типа Option может использоваться для эффективного предотвращения нулевых ссылок (null) исключений. То есть, когда мы вернем некоторые данные в будущем, мы можем вместо этого вернуть тип Option.
определение
В scala тип Option используется для представления необязательных значений. Этот тип данных представлен в двух формах:
-
Some(x): представляет фактическое значение
-
Нет: означает отсутствие значения
- Используя метод getOrElse, когда значение равно None, вы можете указать значение по умолчанию.
Пример 1
Пример описания
- Определите метод деления двух чисел и используйте тип Option для инкапсуляции результата.
- Затем используйте сопоставление с образцом, чтобы распечатать результат.
- не делить на ноль, вывести результат
- Разделить на ноль ошибка исключения печати
Код ссылки
/**
* 定义除法操作
* @param a 参数1
* @param b 参数2
* @return Option包装Double类型
*/
def dvi(a:Double, b:Double):Option[Double] = {
if(b != 0) {
Some(a / b)
}
else {
None
}
}
def main(args: Array[String]): Unit = {
val result1 = dvi(1.0, 5)
result1 match {
case Some(x) => println(x)
case None => println("除零异常")
}
}
Пример 2
Пример описания
- Перепишите приведенный выше случай, используйте метод getOrElse при делении на ноль или значение по умолчанию равно 0
Код ссылки
def dvi(a:Double, b:Double) = {
if(b != 0) {
Some(a / b)
}
else {
None
}
}
def main(args: Array[String]): Unit = {
val result = dvi(1, 0).getOrElse(0)
println(result)
}
5. Частичная функция
Частичные функции могут иметь краткий синтаксис, упрощающий определение функций. В сочетании с функциональным программированием коллекций код можно сделать более элегантным.
определение
-
Частичная функция заключена в фигурные скобки. Набор операторов case без соответствия является частичной функцией.
-
Частичная функция — это экземпляр PartialFunction[A, B].
- A представляет тип входного параметра
- B представляет тип возвращаемого результата
Пример 1
Пример описания
Определите частичную функцию, которая возвращается в соответствии с
входить | возвращаемое значение |
---|---|
1 | один |
2 | два |
3 | три |
разное | разное |
Код ссылки
// func1是一个输入参数为Int类型,返回值为String类型的偏函数
val func1: PartialFunction[Int, String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
println(func1(2))
Пример 2
Пример описания
-
Определить список, содержащий числа от 1 до 10
-
Пожалуйста, преобразуйте все числа 1-3 в [1-3]
-
Пожалуйста, преобразуйте все числа 4-8 в [4-8]
-
Преобразуйте другие числа в (8-*]
Код ссылки
val list = (1 to 10).toList
val list2 = list.map{
case x if x >= 1 && x <= 3 => "[1-3]"
case x if x >= 4 && x <= 8 => "[4-8]"
case x if x > 8 => "(8-*]"
}
println(list2)
6. Регулярные выражения
В scala очень удобно использовать регулярные выражения для сопоставления данных.
определение
Класс регулярных выражений
-
Класс Regex предоставляется в scala для определения регулярных выражений.
-
Чтобы создать объект RegEx, используйте метод r класса String напрямую.
-
Рекомендуется использовать три двойные кавычки для представления регулярных выражений, в противном случае обратную косую черту в регулярном выражении необходимо экранировать.
val regEx = """正则表达式""".r
Метод findAllMatchIn
- Используйте метод findAllMatchIn, чтобы получить все обычные совпадающие строки.
Пример 1
Пример описания
- Определите регулярное выражение, чтобы определить, является ли почтовый ящик законным
- Легальный тест почтового ящика:qq12344@163.com
- Неверный тест электронной почты:qq12344@.com
Код ссылки
val r = """.+@.+\..+""".r
val eml1 = "qq12344@163.com"
val eml2 = "qq12344@.com"
if(r.findAllMatchIn(eml1).size > 0) {
println(eml1 + "邮箱合法")
}
else {
println(eml1 + "邮箱不合法")
}
if(r.findAllMatchIn(eml2).size > 0) {
println(eml2 + "邮箱合法")
}
else {
println(eml2 + "邮箱不合法")
}
Пример 2
Пример описания
Найдите все нелегальные почтовые ящики в списке ниже
"38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com"
Код ссылки
val emlList =
List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")
val regex = """.+@.+\..+""".r
val invalidEmlList = emlList.filter {
x =>
if (regex.findAllMatchIn(x).size < 1) true else false
}
println(invalidEmlList)
Пример третий
Пример описания
-
Существует список следующих почтовых ящиков
"38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com"
-
Используйте регулярные выражения для сопоставления с образцом, чтобы соответствовать имени оператора почтового ящика. Например: электронная почтаzhansan@163.com, вам нужно сопоставить 163
- Используйте круглые скобки для сопоставления групп
-
Распечатайте соответствующий почтовый ящик и оператора
Код ссылки
// 使用括号表示一个分组
val regex = """.+@(.+)\..+""".r
val emlList =
List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")
val emlCmpList = emlList.map {
case x@regex(company) => s"${x} => ${company}"
case x => x + "=>未知"
}
println(emlCmpList)
7. Обработка исключений
Взгляните на следующий фрагмент кода.
def main(args: Array[String]): Unit = {
val i = 10 / 0
println("你好!")
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ForDemo$.main(ForDemo.scala:3)
at ForDemo.main(ForDemo.scala)
Выполнив программу, вы можете увидеть, что scala выдала исключение и не напечатала «привет». Программа завершает работу после возникновения ошибки.
Итак, как решить эту проблему?
В scala для решения этой проблемы можно использовать обработку исключений.
7.1 Перехват исключений
формат синтаксиса
try {
// 代码
}
catch {
case ex:异常类型1 => // 代码
case ex:异常类型2 => // 代码
}
finally {
// 代码
}
- Код в try — это код бизнес-процессинга, который мы написали.
- В catch код, который нужно выполнить при возникновении исключения
- Наконец, код, который будет выполняться независимо от того, возникнет ли исключение
Пример
Пример описания
- Используйте try..catch, чтобы поймать деление на ноль исключений
Код ссылки
try {
val i = 10 / 0
println("你好!")
} catch {
case ex: Exception => println(ex.getMessage)
}
7.2 Генерация исключений
Мы также можем генерировать исключения в методе. Формат синтаксиса подобен Java, используяthrow new Exception...
Выбросить исключение
Пример описания
- генерировать исключение в основном методе
Код ссылки
def main(args: Array[String]): Unit = {
throw new Exception("这是一个异常")
}
Exception in thread "main" java.lang.Exception: 这是一个异常
at ForDemo$.main(ForDemo.scala:3)
at ForDemo.main(ForDemo.scala)
- Scala не нужно объявлять исключения для методов, он уже обращается к проверенным исключениям, которые считаются ошибкой проектирования в Java.
Ниже приведен код Java
public static void main(String[] args) throws Exception {
throw new Exception("这是一个异常");
}
8. Экстрактор
Ранее мы уже использовали очень мощную функцию сопоставления с образцом в scala.Благодаря сопоставлению с образцом мы можем быстро сопоставить переменные-члены в примере класса. Например:
// 1. 创建两个样例类
case class Person(name:String, age:Int)
case class Order(id:String)
def main(args: Array[String]): Unit = {
// 2. 创建样例类对象,并赋值为Any类型
val zhangsan:Any = Person("张三", 20)
val order1:Any = Order("001")
// 3. 使用match...case表达式来进行模式匹配
// 获取样例类中成员变量
order1 match {
case Person(name, age) => println(s"姓名:${name} 年龄:${age}")
case Order(id1) => println(s"ID为:${id1}")
case _ => println("未匹配")
}
}
Возможно ли, чтобы все классы выполняли такое сопоставление с образцом? ответ:
不可以
из. Для поддержки сопоставления с образцом необходимо реализоватьЭкстрактор.
[!NOTE]
Пример класса автоматически реализует методы apply и unapply.
8.1 Определение экстракторов
Ранее мы узнали, что реализация метода apply в объекте-компаньоне класса может быстро создать объект, используя имя класса. В объекте-компаньоне также есть метод unapply. В отличие от apply, unapply разбирает объект этого класса на элементы один за другим.
Чтобы реализовать экстрактор для класса, просто реализуйте метод unapply в объекте-компаньоне класса.
формат синтаксиса
def unapply(stu:Student):Option[(类型1, 类型2, 类型3...)] = {
if(stu != null) {
Some((变量1, 变量2, 变量3...))
}
else {
None
}
}
Пример
Пример описания
- Создайте класс Student с двумя полями, именем и возрастом.
- Реализуйте деструктор класса и используйте выражения сопоставления для сопоставления с шаблоном для извлечения полей в классе.
Код ссылки
class Student(var name:String, var age:Int)
object Student {
def apply(name:String, age:Int) = {
new Student(name, age)
}
def unapply(student:Student) = {
val tuple = (student.name, student.age)
Some(tuple)
}
}
def main(args: Array[String]): Unit = {
val zhangsan = Student("张三", 20)
zhangsan match {
case Student(name, age) => println(s"${name} => ${age}")
}
}
9. Дженерики
Scala, как и Java, поддерживает дженерики для классов, трейтов и методов. Когда мы узнаем о коллекциях, обычно речь идет о дженериках.
scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)
Итак, как вы сами определяете дженерики?
9.1 Определение универсального метода
В scala квадратные скобки используются для определения параметров типа.
формат синтаксиса
def 方法名[泛型名称](..) = {
//...
}
Пример
Пример описания
- Используйте метод для получения среднего элемента массива любого типа
- Прямая реализация без учета дженериков (на основе реализации Array[Int])
- Добавить общую поддержку
Код ссылки
Реализация без учета дженериков
def getMiddle(arr:Array[Int]) = arr(arr.length / 2)
def main(args: Array[String]): Unit = {
val arr1 = Array(1,2,3,4,5)
println(getMiddle(arr1))
}
Добавить общую поддержку
def getMiddleElement[T](array:Array[T]) =
array(array.length / 2)
def main(args: Array[String]): Unit = {
println(getMiddleElement(Array(1, 2, 3, 4, 5)))
println(getMiddleElement(Array("a", "b", "c", "d", "e")))
}
9.2 Общие классы
Классы Scala также могут определять дженерики. Далее, давайте узнаем, как определить универсальные классы scala.
определение
формат синтаксиса
class 类[T](val 变量名: T)
- Определите универсальный класс, добавьте квадратные скобки сразу после имени класса и укажите общие параметры для использования.
- После указания общих параметров, соответствующих классу, используйте эти параметры типа для определения переменных.
Пример
Пример описания
- Реализовать универсальный класс Pair
- Класс Pair содержит два поля, и типы этих двух полей не фиксированы.
- Создавайте объекты универсального класса разных типов и печатайте
Код ссылки
case class Pair[T](var a:T, var b:T)
def main(args: Array[String]): Unit = {
val pairList = List(
Pair("Hadoop", "Storm"),
Pair("Hadoop", 2008),
Pair(1.0, 2.0),
Pair("Hadoop", Some(1.9))
)
println(pairList)
}
9.3 Верхняя и нижняя границы
необходимость:
Когда мы определяем общий тип метода/класса, мы определяем, от какого класса он должен наследоваться или от какого класса он должен быть родительским классом. В этом случае нужно использовать верхнюю и нижнюю границы.
9.3.1 Определение верхней границы
использовать<: 类型名
Указывает на добавлениеВерхняя граница, указывающее, что общий параметр должен наследоваться от класса (или самого себя)
формат синтаксиса
[T <: 类型]
Пример
Пример описания
- Определить класс Person
- Определите класс Student, который наследует класс Person
- Определите демонстрационный общий метод, который получает параметр Array,
- Тип элемента Array, который ограничивает демонстрационный метод, может быть только Person или подклассом Person.
- Тест вызывает демонстрацию, передавая массивы элементов разных типов.
Код ссылки
class Person
class Student extends Person
def demo[T <: Person](a:Array[T]) = println(a)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Student))
// 编译出错,必须是Person的子类
// demo(Array("hadoop"))
}
9.3.2 Нижний мир
Верхняя граница состоит в том, что он должен быть подклассом класса или должен наследовать от класса, а нижняя граница должна бытьродительский класс класса(или сам)
формат синтаксиса
[T >: 类型]
[!NOTE]
Если класс имеет как верхнюю, так и нижнюю границы. Нижняя граница написана спереди, а верхняя граница написана сзади
Пример
Пример описания
- Определить класс Person
- Определите класс Policeman, который наследует класс Person
- Определите класс Superman, который наследует класс Policeman.
- Определите демонстрационный общий метод, который получает параметр Array,
- Тип элемента Array, ограничивающий демонстрационный метод, может быть только Person, Policeman.
- Тест вызывает демонстрацию, передавая массивы элементов разных типов.
Код ссылки
class Person
class Policeman extends Person
class Superman extends Policeman
def demo[T >: Policeman](array:Array[T]) = println(array)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Policeman))
// 编译出错:Superman是Policeman的子类
// demo(Array(new Superman))
}
9.4 Ковариант, контравариант и инвариант
Ковариантность, контравариантность и инвариантность широко используются в исходном коде искры, Изучение этого знания очень поможет нам читать исходный код искры в будущем.
Давайте рассмотрим проблему преобразования типов:
class Pair[T]
object Pair {
def main(args: Array[String]): Unit = {
val p1 = Pair("hello")
// 编译报错,无法将p1转换为p2
val p2:Pair[AnyRef] = p1
println(p2)
}
}
Как сделать так, чтобы класс с дженериками поддерживал преобразование типов?
9.4.1 Инвариант
формат синтаксиса
class Pair[T]{}
- Общие классы по умолчанию неизменяемы
- Тип B является подтипом A, Пара[A] и Пара[B] не имеют никакой принадлежности
- Ява такая же
9.4.2 Ковариация
формат синтаксиса
class Pair[+T]
- Тип B является подтипом A, а Pair[B] можно считать подтипом Pair[A]
- Направление параметризованного типа совпадает с направлением типа.
9.4.3 Инверсия
формат синтаксиса
class Pair[-T]
- Тип B является подтипом A, а Pair[A], в свою очередь, может считаться подтипом Pair[B].
- Направление параметризованного типа противоположно направлению типа
Пример
Пример описания
- Определите суперкласс и подкласс, который наследуется от суперкласса
- Определите три универсальных класса, используя ковариантный, контравариантный и инвариантный соответственно
- Создайте общие классы, чтобы продемонстрировать ковариантность, контравариантность и инвариантность соответственно.
Код ссылки
class Super
class Sub extends Super
class Temp1[T]
class Temp2[+T]
class Temp3[-T]
def main(args: Array[String]): Unit = {
val a:Temp1[Sub] = new Temp1[Sub]
// 编译报错
// 非变
//val b:Temp1[Super] = a
// 协变
val c: Temp2[Sub] = new Temp2[Sub]
val d: Temp2[Super] = c
// 逆变
val e: Temp3[Super] = new Temp3[Super]
val f: Temp3[Sub] = e
}
Используются ковариантность, контравариантность и инвариантность.Изучение этого знания очень полезно для нас, чтобы читать исходный код искры в будущем.
Давайте рассмотрим проблему преобразования типов:
class Pair[T]
object Pair {
def main(args: Array[String]): Unit = {
val p1 = Pair("hello")
// 编译报错,无法将p1转换为p2
val p2:Pair[AnyRef] = p1
println(p2)
}
}
Как сделать так, чтобы класс с дженериками поддерживал преобразование типов?
9.4.1 Инвариант
формат синтаксиса
class Pair[T]{}
- Общие классы по умолчанию неизменяемы
- Тип B является подтипом A, Пара[A] и Пара[B] не имеют никакой принадлежности
- Ява такая же
[Дамп изображения внешней ссылки...(img-Md4Q8pqT-1625207288225)]
9.4.2 Ковариация
формат синтаксиса
class Pair[+T]
- Тип B является подтипом A, а Pair[B] можно считать подтипом Pair[A]
- Направление параметризованного типа совпадает с направлением типа.
9.4.3 Инверсия
формат синтаксиса
class Pair[-T]
- Тип B является подтипом A, а Pair[A], в свою очередь, может считаться подтипом Pair[B].
- Направление параметризованного типа противоположно направлению типа
Пример
Пример описания
- Определите суперкласс и подкласс, который наследуется от суперкласса
- Определите три универсальных класса, используя ковариантный, контравариантный и инвариантный соответственно
- Создайте общие классы, чтобы продемонстрировать ковариантность, контравариантность и инвариантность соответственно.
Код ссылки
class Super
class Sub extends Super
class Temp1[T]
class Temp2[+T]
class Temp3[-T]
def main(args: Array[String]): Unit = {
val a:Temp1[Sub] = new Temp1[Sub]
// 编译报错
// 非变
//val b:Temp1[Super] = a
// 协变
val c: Temp2[Sub] = new Temp2[Sub]
val d: Temp2[Super] = c
// 逆变
val e: Temp3[Super] = new Temp3[Super]
val f: Temp3[Sub] = e
}