Scala: примеры классов, сопоставление с образцом, Option, частичные функции, дженерики (3)

Большие данные
Scala: примеры классов, сопоставление с образцом, Option, частичные функции, дженерики (3)

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. Образец объекта

В основном используется в двух местах:

  1. определить перечисление
  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		// 默认配
}

Пример

Заявление о потребностях

  1. Введите слово из консоли (используя метод StdIn.readLine)
  2. Определите, может ли слово соответствовать следующим словам, если оно может соответствовать, верните предложение
  3. напечатать это предложение
слово возвращение
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): представляет фактическое значение

    1552556858868

  • Нет: означает отсутствие значения

    1552556882910

  • Используя метод 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 разбирает объект этого класса на элементы один за другим.

1552639637165

1552639674932

Чтобы реализовать экстрактор для класса, просто реализуйте метод 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] не имеют никакой принадлежности
  • Ява такая же

1558064807949

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
}