java泛型学习

1.泛型的本质

Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

2.泛型的提出背景

先看一个demo:

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

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList();
       list.add(false);
        list.add(new Student("小明", 20));
        list.add(10086);
        list.add('a');
        for (int i = 0; i < list.size(); i++) {
            String item = (String) list.get(i);
            System.out.println(item);
        }
    }
}

运行结果:Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.company.Main.main(Main.java:12)。很明显,list放入了整型、字符串、布尔类型,然而循环中试图强转为String获取item,编译时却没问题,但运行时却发生类型转换错误。如何避免呢?这就是泛型的引入原因
在这里插入图片描述
即Java集合(Collection)中元素的类型是多种多样的。例如,有些集合中的元素是Byte类型的,而有些则可能是String类型的,等等。Java允许程序员构建一个元素类型为Object的Collection,其中的元素可以是任何类型。在Java SE 1.5之前,没有泛型(Generics)的情况下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要作显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以在预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。因此,为了解决这一问题,J2SE 1.5引入泛型。

3.泛型的作用

3.1第一是泛化。

可以用T代表任意类型Java语言中引入泛型是一个较大的功能增强不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了,这带来了很多好处。

3.2第二是类型安全

泛型的一个主要目标就是提高ava程序的类型安全,使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果不用泛型,则必须使用强制类型转换,而强制类型转换不安全,在运行期可能发生ClassCastException异常,如果使用泛型,则会在编译期就能发现该错误。

3.3第三是消除强制类型转换

泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。

3.4第四是向后兼容

支持泛型的Java编译器(例如JDK1.5中的Javac)可以用来编译经过泛型扩充的Java程序(Generics Java程序),但是现有的没有使用泛型扩充的Java程序仍然可以用这些编译器来编译

4.泛型使用

**针对上面demo 中的问题,我们可以在创建list的时候就预先声明该list存放的是某个确定类型(或者获取时强转为Object也可以),如:

List<String> list1 = new ArrayList<>();
list1.add(1); //无法编译
list1.add(true);//无法编译
list1.add("JAVA");//正确

但我们若想添加整型、布尔型或实体对象即其他类型时,该怎么办?我们可以声明要添加的对象为Object,如:

List<Object> list2 = new ArrayList<>();
list2.add(false);
list2.add(new Student("小明", 20));
list2.add(10086);
list2.add('a');
for (int i = 0; i < list2.size(); i++) {
   Object item = list2.get(i);
   System.out.println(item);
}      

运行结果:
在这里插入图片描述
那泛型该怎么用呢?泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

4.1泛型类

class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  private 泛型标识 /*(成员变量类型)*/ var; 
  .....

  }
}

demo:下面是一个泛型类

public class Test<T> {
    private T member;

    public Test(T member) {
        this.member = member;
    }

    public T getMember() {
        return member;
    }

    public void setMember(T member) {
        this.member = member;
    }

    @Override
    public String toString() {
        return "Test{" +
                "member=" + member +
                '}';
    }
}

主函数测试如下:

public class Main {
    public static void main(String[] args) {
//        testGenericClass1();
          testGenericClass2();
    }

    private static void testGenericClass2() {
        Test test1 = new Test<>("铁心兰");
        Test test2 =new Test<>(110);
        Test test3 =new Test<>(true);
        Test test4 =new Test<>(new Student("小鱼儿",25));
        System.out.println(test1);
        System.out.println(test2);
        System.out.println(test3);
        System.out.println(test4);
    }
    public static void testGenericClass1(){
        Test<String> test1 = new Test<>("铁心兰");
        Test<Integer> test2 =new Test<>(110);
        Test<Boolean> test3 =new Test<>(true);
        Test<Student> test4 =new Test<>(new Student("小鱼儿",25));
        System.out.println(test1);
        System.out.println(test2);
        System.out.println(test3);
        System.out.println(test4);
    }

运行结果:
在这里插入图片描述
定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。

4.2泛型接口

(1)接口:

public interface Query<T> {
    public T queryById(int id);
    public T queryByName(String name);
}

(2)实现类:

public class StuQuery<T> implements Query<T> {
    private Student[] students = new Student[]{
            new Student(1, "妲己", 20),
            new Student(2, "安琪拉", 19),
            new Student(3, "王昭君", 21)
    };
  //这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
   //所以在这个方法中才可以继续使用 T 这个泛型。
    @Override
    public T queryById(int id) {
        for (int i = 0; i < students.length; i++) {
            if (students[i].id == id) {
                return (T) students[i];
            }
        }
        return null;
    }
  //这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
   //所以在这个方法中才可以继续使用 T 这个泛型。
    @Override
    public T queryByName(String name) {
        for (int i = 0; i < students.length; i++) {
            if (students[i].name.equals(name)) {
                return (T) students[i];
            }
        }
        return null;
    }
}

(3)主程序测试结果:

 public static void main(String[] args) {
      tetsGeneriInterface();

  }
 public static void tetsGeneriInterface(){
     StuQuery<Student> stuQuery = new StuQuery<>();
     Student student= stuQuery.queryById(1);
     if (student!=null){
         System.out.println("按id查找结果:"+student);
     }else {
         System.out.println("按id查找结果:未找到");
     }
     Student student2= stuQuery.queryByName("安琪拉");
     if (student2!=null){
         System.out.println("按name查找结果:"+student2);
     }else {
         System.out.println("按name查找结果:未找到");
     }
 }

在这里插入图片描述

4.3泛型方法

简单示例和用法如下:

/**
*这才是一个真正的泛型方法。<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型
* @param t 传入的泛型实参
* @param <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* @return 返回值为T类型
*/
public static <T> T testGenericMethod1(T t){
	System.out.println(t.getClass());
	return t;
}

在这里插入图片描述
例子2,泛型方法与可变参数:

   public static void main(String[] args) {
        testGenericMethod2("貂蝉","杨玉环","西施",1,3.14f,2.5d,false,
        new Student(99,"姜子牙",999));
    }
    /**
     * 
     * @param args 传入的泛型参数
     * @param <MM>声明该方法使用泛型MM
     */
    public static <MM> void testGenericMethod2(MM...args){
        for(MM mm:args){
            System.out.println(mm.toString());
        }
    }

在这里插入图片描述
最后,时间问题,后续补充改进,好瞌睡啊,睡觉喽