(一)泛型学习笔记——基础知识

一、泛型

(官方定义)泛型的出现是JDK 1.5以后,它的本质是参数化类型的应用,可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。

我自己的理解是:泛型类似于方法中参数。只是泛型把数据的类型作为参数传递,就是将当前我们需要操作的数据的类型指定为参数,用到的时候,再具体指定他对应的类型。

在项目开发中我没有尝试过完全不使用泛型的情况,在我开始写代码的时候已经在使用泛型了。虽然没有完全掌握泛型的原理,但已经在简单的使用了。写了个简单的小例子感受了一下。(被举了很多次的例子)

import java.util.ArrayList;
/**
 * 不使用泛型的例子
 * 
 */
public class Generic {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArrayList arrayList=new ArrayList();
        arrayList.add("caka");
        arrayList.add(100);
        arrayList.add(2.3f);
        for (int i = 0; i < arrayList.size(); i++) {
               String temp = (String) arrayList.get(i); 
               System.out.println(temp);
             }
    }

}

运行这段代码报java.lang.ClassCastException异常,强制类型转换的时候出错。这里代码的错误写的很明显,arrayList中默认存放的是Object类型的值,现在存放了三种类型int,float,string,是没有任何问题的。但是在获取arrayList中值的时候,我们无法确定取出的值是什么类型,所以会在运行时出现ClassCastException的异常。

import java.util.ArrayList;
/**
 * 使用泛型的例子
 * 
 */
public class Generic {

    public static void main(String[] args) {
        ArrayList <String> arrayList=new  ArrayList <String>();
        arrayList.add("caka");
        arrayList.add("100");
       // arrayList.add(2.3f);
        for (int i = 0; i < arrayList.size(); i++) {
               String temp = (String) arrayList.get(i); 
               System.out.println(temp);
             }
    }

}

上面第一个例子,可以理解成arrayList在将对象add之后,并不能记录每个add后对象的本身类型,编译的时候全部默认为Object类型了,所以在运行的时候无法确定获取到的下一个值是什么类型。但在使用泛型的时候,会在arrayList申明时给定参数,arrayList.add(1); arrayList.add(2.3f);在编译的时候就会报错。

结合泛型定义,在上个例子中, ArrayList <String> arrayList=new  ArrayList <String>()里String相当于是类型实参,那应该有相应的类型形参。且get()方法的返回结果也直接是此形参类型对应的类型(即类型实参)

二、泛型的定义和使用

泛型可以用于类、接口和方法上(泛型类、泛型接口和泛型方法)

1.泛型类的定义

public class Generic {
    
    public static void main(String[] args) {
        Colleage<String> colleage=new Colleage<String>("undergraduate");  
        String studentName=colleage.getStudent();  
        System.out.println(studentName);  
        colleage.setStudent("postgraduate");
        studentName=colleage.getStudent();
        System.out.println(studentName);
    }
}
//泛型类的定义
class  Colleage<T>{
    private  T student;
    
    public Colleage(T student){
        this.student=student;
    }

    public T getStudent() {
        return student;
    }

    public void setStudent(T student) {
        this.student = student;
    }
}

上面的泛型类中,T表示泛型形参,用于接收来自外部使用时候传入的类型实参,上例中的类型实参就为String类型。泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。


定义的泛型类不一定必须要传入泛型实参,如果传入实参,就会做类型限制。不传入参数也是被允许的,在泛型类中使用泛型的方法可以是任意类型。比如:

Colleage colleage=new Colleage("undergraduate");
String studentName=colleage.getStudent();
System.out.println(studentName);  


之前好奇泛型形参E接受不同传入的泛型实参,生成的对象实例的类型是相同还是不同,测试了一下,发现是相同的。

public class Generic {
    
    public static void main(String[] args) {
        Colleage<String> colleage1=new Colleage<String>("undergraduate"); 
        Colleage<Integer> colleage2=new Colleage<Integer>(985);
        System.out.println(colleage1.getClass()==colleage2.getClass());
    }
}
程序运行结果是true。可见虽然传入了不同的类型实参,但并没有真正意义上生成不同的类型。传入不同泛型实参的泛型类在内存上只有一个,还是上例中的Colleage类型。(涉及类型擦除)

2.泛型接口的定义(简单举例)

public interface GenericInterface<T,U> {

    void getGeneric(T t,U u);
}


实现泛型接口的类没有传入泛型实参的时候,与泛型类的定义相同,在声明类的时候,需要将泛型的声明也一起加到类中。

public class Generic <T,U> implements GenericInterface<T,U> {

    void getGeneric(T t,U u){
        
    }
}


泛型接口的实现类传入了确定的泛型实参。相当于可以生产一个GenericInterface接口,传入无数个实参,

public class Generic implements GenericInterface<String, Double> {
    
    @Override
    public void getGeneric(String s, Double d) {
        System.out.println(s);
        System.out.println(d);
        
    }
    
    public static void main(String[] args) {
        Generic generic=new Generic();
        generic.getGeneric("19960101", 11.11);
        
    }

}
3.泛型方法

public static <T, U> T get(T t, U u) {  
        if (u != null)  
            return u;  
        else  
            return t;  
    }  

泛型方法在声明的时候要在方法返回类型之前声明类型参数

那如果我们在调用泛型方法的时候,有时候需要限制类型参数的类型范围,比如,比较数字大小的方法类型需要限定为Number或者其子类的实例。即有界参数。

public static <T> T maximum(T x,T y,T z) {
                T max=x;
            if(y.compareTo(max)>0){//1
                max=y;
            }
            if(z.compareTo(max)>0){//2
                max=z; 
             }  
                return max; 
       }

上面的方法会在1和2处编译错误。编译之前,还在定义这个泛型方法的时候,泛型类型T的类型不能确定。所以,只能默认T为原始类型Object。所以它只能调用来自于Object的那几个方法,而不能调用compareTo方法。类型限定就很有必要了。由于compareTo方法的使用需要实现Comparable接口。所有该泛型方法需要做如下修改:

public static <T extends Comparable<T>> T maximum(T x,T y,T z) {
                T max=x;
            if(y.compareTo(max)>0){
                max=y;
            }
            if(z.compareTo(max)>0){
                max=z; 
             }  
                return max; 
       }

需要注意的是:

1、不管是限定类还是接口,统一都使用关键字 extends

2、有多个限定需要使用&符号给出多个限定。

public static <T extends Comparable&Serializable> T maximum(T x,T y,T z)

3.如果限定既有接口也有类,那么类必须只有一个,并且放在首位置

public static <T extends Object&Comparable&Serializable> T maximum(T x,T y,T z)




发布了33 篇原创文章 · 获赞 51 · 访问量 9万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章