泛型
1. 回顾Java的泛型
java中的泛型总结如下:
- Java中的泛型使用尖括号
<>
, Scala中使用中括号[]
; - Scala的泛型也和Java中一样,也是不可变的,怎么理解呢,比如Java中:
java
List<Number> list1 = new ArrayList<Number>();
// 编译通不过,等号两边的泛型不一致
List<Number> list2 = new ArrayList<Intger>();
// 编译通不过,等号两边的泛型不一致
List<Number> list3 = new ArrayList<Object>();
// 因此Java中,等号右边的泛型类型可以不写
List<Number> list4 = new ArrayList<>();
- 上面的代码也说明泛型和多态没有关系。
- 泛型用于内部数据类型的约束,在编译时有效,运行时无效。
- 使用过程中,泛型存在上下限的概念:
- 使用数据的时候,如果类型查找的过程是往类的父类查找,表示使用通用性强的类型。这个查找的过程会存在下限的概念。
- 使用数据的时候,如果类型查找的过程是往类的子类查找,表示使用功能不丢失。这个查找的过程会存在上限的概念。
java
public class GenericTypeDemo {
public static void main(String[] args) {
Producer<CharSequence> producer = new Producer<CharSequence>();
Message<? super CharSequence> message = producer.product();
Object content = message.content;
// 编译通不过,找下限,向上找,
// 原因在于String是CharSequence的子类
// String content = message.content;
Consumer<CharSequence> consumer = new Consumer<>();
consumer.consume(new Message<CharSequence>());
consumer.consume(new Message<String>());
// 编译通不过,找上限,向下找,Object是父类,所以找不到
// consumer.consume(new Message<Object>());
}
}
// T是target的简写,这里用来承载数据data
class Message<T>{
T content;
}
class Consumer<T> {
public void consume(Message<? extends T> message){
}
}
class Producer<T>{
public Message<? super T> product(){
return null;
}
}
2. 协变和逆变
泛型没有多态的概念,为了开发方便,整合概念,scala对泛型进行了语法补充。
- 如果类型不变,但是泛型存在多态(父子关系),那么(类型+泛型)也存在多态(父子关系),如果实现这样的功能,那么泛型就发生了变化,这种变化称之为协变。协变的基本语法,就是在泛型的前面增加特殊符号:+
- 如果类型不变,但是泛型存在多态(父子关系),那么(类型+泛型)也存在多态(子父关系),如果实现这样的功能,那么泛型就发生了变化,这种变化称之为逆变。逆变的基本语法,就是在泛型的前面增加特殊符号:-
scala
def main(args: Array[String]): Unit = {
val message1: Message[CharSequence] = new Message[CharSequence]
// 协变,可以理解为左右两边的类型变为MessageCharSequence = MessageString,
// 现在父子关系是MessageCharSequence为父类,MessageString为子类
val message2: Message[CharSequence] = new Message[String]
// 逆变,可以理解为左右两边的类型变为MessageCharSequence = MessageObject,
// 现在子父关系是MessageCharSequence是子类,MessageObject为父类
// 于是MessageCharSequence = MessageObject的写法就编译通不过了,因为多态语法父类要写在前面
val message3: Message[CharSequence] = new Message[Object]
}
}
// T是target的简写,这里用来承载数据data
class Message[+T]{
}
3. 上限和下限
Scala中的泛型也存在上限和下限的概念,但是不是采用关键字,而是采用颜文字。
3.1上限
scala
// 定义一个基类
class Animal
// 定义几个子类
class Dog extends Animal
class Taidi extends Dog
// 定义一个泛型函数,其类型参数 T 必须是 Animal 的子类
def processAnimal[T <: Dog](dog: T): Unit = {
println(s"逻辑代码执行中,参数的真实类型是: ${dog.getClass.getSimpleName}")
}
// 定义一个泛型函数,其类型参数 T 必须是 Animal 的父类
def handAnimal[T >: Dog](dog: T): Unit = {
println(s"逻辑代码执行中,参数的真实类型是: ${dog.getClass.getSimpleName}")
}
def main(args: Array[String]): Unit = {
// 使用
val dog = new Dog
val taidi = new Taidi
val animal = new Animal
processAnimal(dog) // 正确
processAnimal(taidi) // 正确
//processAnimal(animal) // 编译错误,因为 animal 不是 Dog 的子类
handAnimal(animal)
handAnimal(dog)
// 这里执行成功的原因是
// 这里参数类型是Dog的父类,也就可以是Any类型, 然而Taidi虽然是Dog子类,但也是Any类型,是Any类的实现
// 编译器进行类型推导, 所以执行不报错,
// 可以认为这里的下限并不是限定具体是哪个类型,什么类型都可以传入
handAnimal(taidi)
}
运行结果: