泛型,类型通配符(十八)

306 阅读6分钟

泛型

1 什么是泛型?为什么使用泛型?

​ Java中会有一些容器(数组以外的容器),它在设计之初,并不知道这个容器实际用来装什么对象。可能用来装学生对象,可能用来装图形对象。如果这个容器没有”标签“,那么我们从容器中取数据使用的时候很麻烦。这个泛型就可以比喻成”标签“。

​ 有标签之后可以使得代码更安全(不是该类型不接收),也更简便(取出来的时候不用考虑类型,也不用强制类型转换)。

形参是用来做什么的?

形参就是由方法的使用者来决定具体的值

同理,当我们设计某个类的成员,或者是某个方法的参数类型等时,不清楚具体的类型,我们希望使用者来确定成员或参数的具体类型。把类型作为参数传递,泛型就是叫做参数化类型。

2 泛型的格式

<类型> 就是泛型。

3 泛型的分类

泛型分为两种

(1)泛型类与泛型接口

(2)泛型方法

4 泛型类与泛型接口

(1)语法格式

在声明类或者接口时,在类名或接口名后面用<T>形式

【修饰符】 class 类名<T>{
	
}
【修饰符】 implement 接口名<T>{
	
}

说明:

①泛型字母不仅限于T,可以是别的。但是一般都是单个的大写字母。

②类或者接口上面定义的<T>泛型,可以用于声明类的成员变量,方法的形参,返回值类型等。

③类或者接口上面定义的<T>泛型,不能用于基本数据类型。

④<T>类型只能指定为引用数据类型,不支持基本数据类型,如果是基本数据类型,要改用对应的包装类。

⑤如果这个类或者这个接口有多个未知的类型需要用泛型表示,我们可以在<>中写多个类型。

T 代表type,类型的统称

E 代表element,元素类型

K 代表key,键的类型

V 代表value,值的类型

⑥如果这个泛型<T>不是代表任意引用数据类型,而是某种类型范围内,

例如:学生的成绩不是任意引用数据类型都行,而且必须是Number类型范围内,可以给<T>限定上限。<T extends 上限>。上限可以多个类型,但是多个类型中,类类型的只能有一个,接口类型多个。而且类在左边,接口在右边,多个类型之间使用&连接。

(2) 什么时候可以给<T>泛型确定具体的类型?

①new对象时

②实现泛型接口时

③继承泛型类时

(3)子类或者实现类可以延续父类过着父接口的泛型<T>

(4)当我们使用泛型类或者泛型接口时,没有给<T>指定具体类型,那么编译器会进行自动处理,

​ 如果<T>声明时,没有指定上限,那么就按照Object类型处理,这种情况称为泛型擦除。

​ 如果<T>声明时,指定了上限,那么就按照第一个上限处理。

泛型方法

1 为什么要用泛型方法?

(1)在类或接口上面声明的<T>泛型是不能用于静态成员上。 那么如果某个静态方法想要使用泛型,怎么办?可以单独声明自己的泛型。

(2)当多个方法的形参类型未知,但是多个方法之间的形参类型是无关,可以单独声明泛型。

2 如何声明泛型方法?

【修饰符】 <T> 返回值类型 方法名(【形参列表】)【throws 异常列表】{

}

说明:

(1)<T>类型和声明泛型类一样,可以是多个。

例如:<T>、<T,U>

(2)<T>类型和声明泛型类一样,可以指定上限。

​ 例如:<T extends 上限>

(3)这个<T>仅限于当前方法使用

类型通配符

1 为什么要使用通配符?

​ 当我们使用泛型类/接口的类型,去声明一个变量或者形参时。此时仍然无法确定<T>对应的具体类型时,可以使用<?>代替,代表任意引用类型。

2 通配符的使用形式有三种:

(1)<?> :?代表任意引用类型。

(2)<? extends 上限>:?代表是上限本身或者它的子类类型。

(3)<? super 下限>:?代表是下线类型本身或者它的父类类型。

​ 表示上限用extends,只是表示?是属于这个类型的系列,?可以是上限的子类,或者是实现类,或者是子接口。

3 <? >

public class TestWild {
    public static void main(String[] args) {
        //语文老师,给张三填写完成绩
        XueYuan<String> x1 = new XueYuan<>("张三","优秀");
        //数学老师,给张三填写完成绩
        XueYuan<Double> x2 = new XueYuan<>("张三",85.5);
        //英语老师,给张三填写完成绩
        XueYuan<Character> x3 = new XueYuan<>("张三",'A');
        //现在想要用一个数组,把这三个对象都装起来
        XueYuan<?>[] arr = new XueYuan[3];
        arr[0] = x1;
        arr[1] = x2;
        arr[2] = x3;
    }
}

/*
做一个复杂版的学生成绩管理系统:
A:语文老师说,他对学生的成绩评定是:优秀、良好、及格、不及格
B:数学老师说,他对学生的成绩评定是:85.6,98.5...
C:英语老师说,他对学生的成绩评定是:A,B,C,D...
D:体育老师说,他对学生的成绩评定是:true,false

这里把成绩的类型,用T表示。
如果说成绩T类型不是任意引用数据类型,只能是Number或它的子类类型,那么<T>可以限定上限
<T extends Number>
 */
class XueYuan<T>{
    private String name;
    private T score;

    public XueYuan() {
    }

    public XueYuan(String name, T score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "XueYuan{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

4 <? extends 上限>

/*
需求2:
例如:我们要声明一个学生管理类,这个管理类要包含一个方法,
找出学生数组中成绩最高的学生对象。
要求学生的成绩的类型必须可比较大小,实现Comparable接口。


 */
package com.atguigu.generic;

public class TestWild2 {
    public static void main(String[] args) {
        XueYuan<? extends Comparable>[] arr2 = new XueYuan[3];
        arr2[0] = new XueYuan<>("张三",89.5);
        arr2[1] = new XueYuan<>("李四",85.5);
        arr2[2] = new XueYuan<>("王五",86.5);
        XueYuanService service = new XueYuanService(arr2);
        XueYuan<?> max = service.max();
        System.out.println(max);
    }
}

5 <? super 下限>

需求3:
现在要声明一个数组工具类,包含可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,
而且这个定制比较器对象可以是当前数组元素类型自己或其父类的定制比较器对象

 */
package com.atguigu.generic;

import java.util.Comparator;

public class TestWild3 {
    public static void main(String[] args) {
        Yuan[] all = new Yuan[3];
        all[0] = new Yuan(1.5);
        all[1] = new Yuan(3.5);
        all[2] = new Yuan(0.5);

        MyArrays.sort(all,new GraphicComparator());
    }
}
class GraphicComparator implements Comparator<Graphic> {

    @Override
    public int compare(Graphic o1, Graphic o2) {
        return Double.compare(o1.area(),o2.area());
    }
}

abstract class Graphic{
    public abstract double area();
}
class Yuan extends Graphic{
    private double radius;

    public Yuan(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    @Override
    public String toString() {
        return "Yuan{" +
                "radius=" + radius +
                '}';
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}