Skip to content

泛型

1. 回顾Java的泛型

java中的泛型总结如下:

  1. Java中的泛型使用尖括号<>, Scala中使用中括号[];
  2. 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<>();
  1. 上面的代码也说明泛型和多态没有关系。
  2. 泛型用于内部数据类型的约束,在编译时有效,运行时无效。
  3. 使用过程中,泛型存在上下限的概念:
  • 使用数据的时候,如果类型查找的过程是往类的父类查找,表示使用通用性强的类型。这个查找的过程会存在下限的概念。
  • 使用数据的时候,如果类型查找的过程是往类的子类查找,表示使用功能不丢失。这个查找的过程会存在上限的概念。
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对泛型进行了语法补充。

  1. 如果类型不变,但是泛型存在多态(父子关系),那么(类型+泛型)也存在多态(父子关系),如果实现这样的功能,那么泛型就发生了变化,这种变化称之为协变。协变的基本语法,就是在泛型的前面增加特殊符号:+
  2. 如果类型不变,但是泛型存在多态(父子关系),那么(类型+泛型)也存在多态(子父关系),如果实现这样的功能,那么泛型就发生了变化,这种变化称之为逆变。逆变的基本语法,就是在泛型的前面增加特殊符号:-
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)
}

运行结果:
Alt text