Java泛型

目录

定义使用泛型的类:

定义使用泛型的方法:

传递给泛型的类型:

定义使用泛型的接口:

同时定义多个泛型的类型变量:

泛型通配符:

泛型的限定:


泛型是一个未知的数据类型,当我们不确定什么什么数据类型的时候,可以使用泛型泛型可以接收任意的数据类型。

定义使用泛型的类:

class 类名<泛型名称> extends .... implements ....{

}

这样就可以将泛型当作一个数据类型来使用。

示例:

class Demo<E>{
    E name;
}

该类可定义普通的不含泛型的方法,也可定义使用泛型的静态方法或实例方法。

当创建该类的对象时,可以确定泛型的数据类型:

class Main {
    public static void main(String[] args) {
        Demo<String> a1 = new Demo<String>();    // 这样name变量就是String类型的
        Demo<Student> b1 = new Demo<>();    // 第二个尖括号的类型可省略
        // Demo<> b1 = new Demo<Student>();  但是这样不行
    }
}

class Demo<E>{
    E name;
}

定义使用泛型的方法:

如果类没有定义泛型: 

修饰符列表 <泛型> 返回值类型 方法名 ( 参数列表 ){
    方法体;
}

但如果类定义了泛型,那么方法可以之间使用该泛型,因为该泛型现在已经看成是一个具体的类型了,如:

public E kk(E e){

}

参数列表可以使用泛型来定义变量。

在调用方法的时候,传递什么类型的参数,泛型就是什么类型。

注意:静态方法不可以访问类上定义的泛型,所以定义泛型的静态方法和定义泛型的实例方法有所区别:

class Demo<E>{
    public  static <E> E ff1(E e){
        return e;
    }

    public  static <SuiBian> SuiBian ff2(SuiBian e){
        return e;
    }

    public E ff3(E e){    // 使用类定义的泛型
        return e;
    }
    
    public <T> T ff4(T e){    // 使用自定义的泛型
        return e;
    }
}

在调用静态方法时,我们可以指定方法的泛型:

class Demo{
    public static void main(String[] args) {
        System.out.println(A.<String>get("haha","nihao"));
    }
}

class A{
    public static <T> T get(T...a){
        return a[a.length/2];
    }
}

大多数情况下,编译器可以通过所提供的实参来判断泛型应该取什么类型,但在某些特殊情况下我们也需要指定某个类型。

如:

class Demo{
    public static void main(String[] args) {
        System.out.println(A.<Number>get(1.1,100));
    }
}

class A{
    public static <T> T get(T...a){
        return a[a.length/2];
    }
}

因为如果不指定泛型,编辑器发现这个代码有两种解释方式,且都是合法的。简单来说,编译器将参数自动装箱为一个Integer类型和一个Double类型,为了兼容,它会去查找这些类的共同父类,他找到了两个父类,一个是Number抽象类,一个是Comparable接口,在这种情况下,如果我们不想改变我们所传入的值,那么我们就要指定一个他们的共同父类。

同样的,实例方法也可以这样做:

class Demo{
    public static void main(String[] args) {
        System.out.println(new A().<Number>get(1.1,100));
        System.out.println(new B().<Number>get(1.1,100));
    }
}

class A{
    public <T> T get(T...a){
        return a[a.length/2];
    }
}

class B<T>{
    public T get(T...a){
        return a[a.length/2];
    }
}

传递给泛型的类型:

1、传递给泛型类型时必须使用构造数据类型,不能传递基本数据类型给泛型。所以这时候就要用到我们的包装类,int类型可以传递Integer类型,有无自定义的类也是构造数据类型,所以也可以传递类名,如student等。

2、当在调用具有泛型的类或方法时,可以选择不传递任何类型,这样泛型默认为Object类型。

定义使用泛型的接口:

interface 接口名<泛型名称> {

}

实现类可以指定泛型的类型,这样所有使用了该泛型的抽象方法都有了具体的类型,如:

class 类名 implement 接口名<String> {

} 

实现类也可以不指定泛型的类型,等创建类对象或调用方法的时候再指定,如:

class 类名<泛型名称> implement 接口名<泛型名称> {

}

当然如果是函数式接口,那么还可以使用lambda来实现:

class Main{
    public static void main(String[] args) {
        Demo<String> d1 = (s)->{    // 指定类型
            return s;
        };

        Demo d2 = (s)->{    // 不指定类型
            return s;
        };
    }
}

interface Demo<E>{
    E ff3(E e);
}

同时定义多个泛型的类型变量:

class Demo<E,T>{
    E name;
    T age;
}

可在尖括号内定义多个泛型的类型变量。

泛型通配符:

假设我们要定义一个可以遍历所有ArrayList集合的方法,且该方法可以对存储任何类型的集合使用,那么这时候就要用到泛型的通配符:

public static <T> void printArray(T arrayList){    
    Iterator iterator = arrayList.iterator();    // 报错。找不到iterator方法
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

在上述代码中,因为Java不知道 T 是什么类型的,所以不知道 T 类中有无iterator这个方法,所以报错。但如果为ArrayList<String>类型的形参定义一个方法,那就“写死了”,不能对ArrayList<Integer>类型的参数进行操作,但如果使用方法重载又要浪费大量的代码得不偿失。这时候就可以用到我们的通配符来表示泛型的未知类型了:

import java.util.ArrayList;
import java.util.Iterator;

class Main{
    public static void main(String[] args) {
        ArrayList<String> s = new ArrayList<>();
        s.add("AA");
        s.add("BB");
        ArrayList<Integer> i = new ArrayList<>();
        i.add(100);
        i.add(200);

        printArray(s);
        printArray(i);
    }

    public static void printArray(ArrayList<?> arrayList){
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
/*
    public static void printArray(ArrayList<Integer> arrayList){    只能遍历Integer类型的集合
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    public static void printArray(ArrayList<String> arrayList){     只能遍历String类型的集合
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
*/    
}

那么为什么不能把 printArray 方法的参数类型设置成 ArrayList<Object> 呢?因为在泛型中,是没有继承这个概念的。

当然我们也可以使用这样的形式:

public static <E> void Demo(Collection<E> c){
        
}

但这样就必须在方法中定义使用的泛型了。一般如果在方法中不使用Collection的<>中的类型的话,例如调用变量c的方法,一般使用通配符 ? 即可。

传递类型给泛型时是不允许使用通配符的,即不能创建对象时使用,只能作为方法的参数使用,表示未知的泛型类型。

ArrayList<?> unKnow = new ArrayList<>();
unKnow.add("aa");    // 报错,'java.util.ArrayList' 中的 'add(capture<?>)' 无法应用于 '(java.lang.String)'

通配符不是类型变量,所以也不能作为一种类型使用:

? t = p.getFirst();    // 报错

泛型的限定:

如果需要的话,通配符还可以这样定义:

public static void printArray(ArrayList<? extends Number> arrayList){
    Iterator iterator = arrayList.iterator();    限定泛型所属的类必须是Number的子类
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

public static void printArray(ArrayList<? super Number> arrayList){
    Iterator iterator = arrayList.iterator();    限定泛型所属的类必须是Number的父类
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

public static <T extends Comparable> Pair<T> maxmin(){
    ......        // 限定泛型必须是Comparable的子类,即实现了Comparable接口
    return new Pair<>(min,max)
}

这样会对泛型的类型加以限定。

注意:在Java的限定中,无论是不是接口,都是用extends关键词表示,不会用implement关键词。

代码示例: 

import java.util.ArrayList;

class Main{
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<Number> list3 = new ArrayList<>();
        ArrayList<Object> list4 = new ArrayList<>();
        
        // Integer extends Number extends Object
        // String extends Object

        Extend(list1);
        Extend(list2);  // 报错
        Extend(list3);
        Extend(list4);  // 报错
        
        Super(list1);   // 报错
        Super(list2);   // 报错
        Super(list3);
        Super(list4);
    }

    public static void Extend(ArrayList<? extends Number> a){

    }

    public static void Super(ArrayList<? super Number> a){

    }

}

不只是通配符,其余泛型类型也可以使用限定:

类:

class Demo<T extends List> {

}

方法:

public static <T extends Number> void Demo(T c){

}

接口:

interface demo<T extends Number>{
    
}

 我们可以对一个泛型类型or通配符进行多个限制:

public static <T extends ArrayList & Comparable> Pair<T> maxmin(){
    ......        // 限定泛型必须是ArrayList的子类,还实现了Comparable接口
    return new Pair<>(min,max)
}

注意:在对泛型的限定中,我们最多只能限定一个类,但可以限定多个接口。且一个类作为限定,那么这个类必须放在限定的首位。

public static <T extends Comparable & ArrayList> Pair<T> maxmin(){
    ......        // 报错!
    return new Pair<>(min,max)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦矜

对你有帮助的话,请我吃颗糖吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值