泛型类、泛型方法、类型通配符的使用

泛型概念的提出(为什么需要泛型)?
参考博文
https://www.cnblogs.com/lwbqqyumidi/p/3837629.html
https://www.cnblogs.com/coprince/p/8603492.html
首先,我们看下下面这段简短的代码:

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("张三");
        list.add("李四");
        list.add(10);

        for(int i = 0; i < list.size(); i++){
            String str = (String) list.get(i);
            System.out.println(str+" ");
        }

        System.out.println();
    }
}

定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型。在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值或其他编码原因,很容易出现类似于//1中的错误。因为编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。

在如上的编码过程中,我们发现主要存在两个问题:

1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。

2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。
什么是泛型
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
用泛型对上面出现的问题进行解决;

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList();   //规定list集合为String类型
        list.add("张三");
        list.add("李四");
       /* list.add(10);*/      此处在先这样写编译就会报错 应为10 不是String类型

        for(int i = 0; i < list.size(); i++){
            String str = (String) list.get(i);
            System.out.println(str+" ");
        }

        System.out.println();
    }
}

采用泛型写法后,在//1处想加入一个Integer类型的对象时会出现编译错误,通过List,直接限定了list集合中只能含有String类型的元素,从而在//2处无须进行强制类型转换,因为此时,集合能够记住元素的类型信息,编译器已经能够确认它是String类型了。

泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

class Generic<T,V>{  //制定多个泛型类型
    private T t;					
    private V v;

    public void setV(V v) {
        this.v = v;
    }

    public void setT(T t) {
        this.t = t;
    }

    public V getV() {
        return v;
    }																/*这里虽然使用泛型但并不是泛型方法,这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。*/

    public T getT() {
        return t;
    }
    @Override
    public String toString() {
        return "generic{" +
                "t=" + t +
                ", v=" + v +
                '}';
    }
}
public class demo1 {
    public static void main(String[] args) {
       Generic<String,Integer> generic = new Generic<String,Integer>();
        generic.setT("张三");
        generic.setV(30);
        System.out.println(generic);
    }
}

泛型方法
你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则: 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)

class Generic<T,V>{  //制定多个泛型类型
    private T t;
    private V v;

    public Generic() {
        super();
    }

    public Generic(T t, V v) {
        this.t = t;
        this.v = v;
    }

    public void setV(V v) {
        this.v = v;
    }

    public void setT(T t) {
        this.t = t;
    }

    public V getV() {
        return v;
    }

    public T getT() {
        return t;
    }
    @Override
    public String toString() {
        return "generic{" +
                "t=" + t +
                ", v=" + v +
                '}';
    }

    public <T> void genericMethod(T t){  //泛型方法
        System.out.println(t);
    }

}
public class demo1 {
    public static void main(String[] args) {
        Generic<String,Integer> generic = new Generic<String,Integer>();
        generic.setT("张三");
        generic.setV(30);
        generic.genericMethod("lis");
        System.out.println(generic);
    }
}

泛型通配符
参考文章
https://www.cnblogs.com/alsf/p/5690052.html
通配符只有在修饰一个变量时会用到,使用它可方便地引用包含了多种类型的泛型;
复制代码

public static void main() {

    //不使用通配符
    ArrayList<Object> arr = new ArrayList<Object>();
    // ArrayList<Object> arr = new ArrayList<String>(); 编译不通过,arr只能引用包含Object的集合

    //使用通配符
    ArrayList<?> arr2;
    arr2 = new ArrayList<String>();
    arr2 = new ArrayList<Integer>();
    arr2.get(0);    //返回的,是一个Object对象,通配符会使原集合包含类型信息丢失,也是通配符的使用代价

// 通常在方法参数中才会使用通配符,使得这个方法可以引用多种泛型集合。这个和范型方法不一样,这里只是一个引用变量
void gMethod(ArrayList<? extends Number> param) {
}

复制代码
通配符的extends super关键字
 之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。

设置上限
复制代码


class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class GenericsDemo17{
    public static void main(String args[]){
        Info<Integer> i1 = new Info<Integer>() ;        // 声明Integer的泛型对象
        Info<Float> i2 = new Info<Float>() ;            // 声明Float的泛型对象
        i1.setVar(30) ;                                    // 设置整数,自动装箱
        i2.setVar(30.1f) ;                                // 设置小数,自动装箱
        fun(i1) ;
        fun(i2) ;
    }
    public static void fun(Info<? extends Number> temp){    // 只能接收Number及其Number的子类
        System.out.print(temp + "、") ;
    }
};

运行成功。但是,如果传人的泛型类型为String的话就不行,因为String不是Number子类。

在类中使用泛型上限。

package Thread1;
class Info<T extends Number>{    // 此处泛型只能是数字类型
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class demo1{
    public static void main(String args[]){
        Info<Integer> i1 = new Info<Integer>() ;        // 声明Integer的泛型对象
    }
};

如果在使用Info的时候设置成String类型,则编译的时候将会出现错误(String不是Number子类):
设置下限

class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class GenericsDemo21{
    public static void main(String args[]){
        Info<String> i1 = new Info<String>() ;        // 声明String的泛型对象
        Info<Object> i2 = new Info<Object>() ;        // 声明Object的泛型对象
        i1.setVar("hello") ;
        i2.setVar(new Object()) ;
        fun(i1) ;
        fun(i2) ;
    }
    public static void fun(Info<? super String> temp){    // 只能接收String或Object类型的泛型,String类的父类只有Object类
        System.out.print(temp + "、") ;
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_43904316/article/details/88928079