Java泛型详解

目录

  • 友情提醒
  • 第一章、泛型介绍
    • 1.1)什么是泛型
    • 1.2)泛型为什么出现
  • 第二章、泛型类/接口/方法
    • 2.1)为什么要用泛型类/接口
    • 2.2)自定义泛型类
    • 2.3)多泛型变量
    • 2.4)自定义泛型接口
    • 2.5)自定义泛型方法
    • 2.6)泛型方法的应用
  • 第三章、泛型通配符和泛型限定
    • 3.1)泛型的通配符
    • 3.2)泛型的限定

友情提醒

先看文章目录,大致了解知识点结构,直接点击文章目录可以跳转到文章指定位置。

第一章、泛型介绍

1.1)什么是泛型

百度百科:Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型(通过泛型指定的类型来控制形参具体的类型),操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

①泛型的格式: <具体的数据类型>
② 集合加泛型以后,集合中只能保存具体的某一种数据类型。

ArrayList<String> list1 = new ArrayList<>();

③泛型只支持引用数据类型,不支持基本数据类型。

//报错
// ArrayList list2 = new ArrayList();

④等号左右两侧<>内的泛型实参类型不支持多态性,只能是相同类型

//报错
// List list3 = new ArrayList();

1.2)泛型为什么出现

①集合中是可以保存任意类型的对象。这些对象一旦保存到集合中之后,都会被提升成Object类型,通过对类型Object的引用来实现参数的“任意化”。
②“任意化”带来的缺点是取出数据时要显示的强制向下类型转换,可是取数据时我们并不知道是什么类型,使用保存的对象的特有方法或者属性时,需要向下转型并且向下转型还有风险,还得使用 instanceof关键字进行判断。

ArrayList list = new ArrayList();
        list.add("Java");
        list.add("python");
        list.add("mysql");

//如下集合中添加其他引用数据类型,但是向下转型时我们不知道是什么类型数据,容易出错
//运行时才提示ClassCastException
//   list.add(new Object());
        
        for (Object o : list) {
//由于集合可以存储任意类型的对象,而这里只是适合String类型的强制类型转换,
//加入其他数据类型运行时会报classCastException类转换
            String str = (String) o;
            System.out.print(str.length()+"  ");
        }

总之很麻烦。于是出现了泛型解决这个问题

ArrayList<String> list = new ArrayList<>();
        list.add("Java");
        list.add("python");
        list.add("mysql");
        list.add("html");
        list.add("javascript");
//加入泛型,变量的类型被限制,若加入其他引用类型编译阶段提示错误,add String in ArrayList cannot be applied to Object
//而不是运行时提示ClassCastException
 // list.add(new Object());	
 
        for (String s : list) {
            System.out.println(s.length());
        }

泛型的好处:
①解决了集合中存储数据的不安全性;
②避免了运行时的异常,把运行时可能发生的异常,放在编译时作为编译错误处理了;
③省略了代码中的强制类型转换的书写;

第二章、泛型类/接口/方法

2.1)为什么要用泛型类/接口

①在类名或接口名后面使用<标识符>,在使用的时候,可以指定其中的类型。重点:使用的时候,指定其中的类型
②注意:不可以定义泛型数组
比如典型的容器类:在ArrayList类上有一个泛型的参数:E
在这里插入图片描述
当我们使用ArrayList的时候,我们要存储String类型就可以进行指定:

//指定ArrayList集合存储String类型
ArrayList<String> list = new ArrayList<>();

2.2)自定义泛型类

①在类名或接口名后面使用<标识符>,标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。
②泛型类不可以继承Exception类,即泛型类不可以作为异常被抛出。
③不可以用泛型构造对象,即:first = new T();是错误的
例子一:创建对象的时候,可以指定成员变量类型

//自定义泛型类:
class Demo<T>{
//num这个成员变量的类型为T,类型由外部指定 
    T num;

    public void func(T num){
       System.out.println(num);
    }
}
// ----------------------------分割----------------------------------
public class TestGeneric_2 {
    public static void main(String[] args) {
        //创建Demo对象的过程中,明确泛型形参的具体类型 --> 定义泛型实参类型
        //Demo d = new Demo<>(); T被指定为string了
        Demo<String> d = new Demo<>();
        d.func("abc");//abc
        
		//Demo d = new Demo<>(); T被指定为Double了
        Demo<Double> d2 = new Demo<>();
        d2.func(123.23);//123.23

    }
}

例子二:创建对象的时候,可以指定成员变量类型

//通过泛型,每次创建Student对象都可以指定成员变量score 的类型
class Student<E>{
//score这个成员变量的类型为E,类型由外部指定 
     E score;
    public Student( E score) {//泛型构造方法形参的类型也为E,E的类型由外部指定
        this.score = score;
    }
    public Student() { }
    @Override
    public String toString() {
        return "Student{" + " score=" + score + '}';
    }
}
// ----------------------------分割----------------------------------
public class TestGeneric_3 {
    public static void main(String[] args) {
    //通过泛型,每次创建Student对象都通过构造方法指定成员变量score 的类型
        Student<String> s1 = new Student<>( "优秀");
    //必须是类类型,不能是基本类型
        Student<Integer> s2 = new Student<>( 88);
        Student<Character> s3 = new Student<>('B');
        System.out.println(s1);//Student{ score=优秀}
        System.out.println(s2);//Student{ score=88}
        System.out.println(s3);//Student{ score=B}
    }
}

2.3)多泛型变量

ps:
在原来的T后面用逗号隔开,写上其它的大写字母

//在原来的T后面用逗号隔开,写上其它的大写字母
public class More<T,U> {
    private  T x;
    private T y;
    private U name;
    
    public T getX() {   return x;    }
    public void setX(T x) {  this.x = x; }
    public T getY() {   return y;  }
    public void setY(T y) {  this.y = y; }
    public U getName() { return name;  }
    public void setName(U name) {  this.name = name;    }
    public More() {    }
    public More(T x, T y, U name) {
        this.x = x;
        this.y = y;
        this.name = name;
    }

public static void main(String[] args) {
        //使用
More<Integer,String> morePoint = new More<Integer, String>(12,23,"尔康");
System.out.println(morePoint.getX()+morePoint.getY()+morePoint.getName());//35尔康
    }
}

2.4)自定义泛型接口

①在类名或接口名后面使用<标识符>,标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。

//自定义泛型接口
interface Inter<T>{
    public abstract T show(T t);
}
// ----------------------------分割----------------------------------
//类实现接口的过程中明确泛型形参的具体类型
class InterImpl implements Inter<String>{
    @Override
    public String show(String s) {
    System.out.println(s);
        return s;
    }
}
// ----------------------------分割----------------------------------
//类实现接口的过程中没有明确泛型形参的具体类型
class InterImpl2<E> implements Inter<E> {
    @Override
    public E show(E e) {
    System.out.println(e);
        return e;
    }
}
// ----------------------------分割----------------------------------
public class TestGeneric_5 {
    public static void main(String[] args) {
        //在实例化对象的过程中明确泛型形参的具体类型
        InterImpl2<Integer> ii = new InterImpl2<>();
        ii.show(123);//123

        //在实例化对象的过程中去除了<>的定义 ==> 称为:泛型的擦除操作
        //理解:所有的泛型形参类型位置都同步为Object类型
        InterImpl2 ii2 = new InterImpl2();
        ii2.show("abc");//abc
    }
}

2.5)自定义泛型方法

①有时方法需要接收的数据类型和类上外界指定的类型不一致。这时我们可以在这个类中的这个方法上单独给这个方法设定泛型。
②静态方法中不能使用类的泛型,因为类的泛型是在实例化之后才创建,而静态方法是在实例化之前就被创建了的。一个存在的东西不能调用一个不存在的东西。

class Demo2<A>{
    //静态方法中不能使用类的泛型,因为类的泛型是在实例化之后才创建,而静态方法是在实
    // 例化之前就被创建了的。一个存在的东西不能调用一个不存在的东西。
   // public static void func(A a) {     System.out.println(a); }//这会报错噢
    public  void func(A a) {     System.out.println(a); } //这不会

}

③泛型方法,可以声明为静态的。原因:泛型参数是在调用的时候确定的,并非是在实例化类的时候确定。

class Demo2<A>{
    public  void func(A a) {     System.out.println(a); }
    public static <B> void method(B b) {
        System.out.println(b);
    }
}

//------------------------分割------------------------------
public class TestGeneric_4 {
    public static void main(String[] args) {
        //实例化对象的过程中,就明确了泛型形参A的具体类型,但是B类型仍然不明确
        Demo2<String> d = new Demo2<>();
        d.func("hello");
        //在调用method方法传入实参数据的一刹那,B类型的具体类型就明确了
        d.method(123);
	}
}

④泛型方法只是针对于方法本身来讲,与方法所在的类或接口是否为泛型类、泛型接口没有关联。

public class TestGeneric_6 {
    public static void main(String[] args) {
        method(123);
    }
    public static  <B> void method(B b) {
        System.out.println(b);
    }
}

2.6)泛型方法的应用

    //自定义泛型方法     遍历不同类型的数组元素
    public static <T> void printArray(T[] arr) {    
        for (T t : arr) {
            System.out.print(t + " ");
        }
        System.out.println();
    }
//------------------------分割-----------------------    
 public static void main(String[] args) {
        String[] arr = {"aaa", "bbb", "ccc", "ddd"};
        Integer[] arr2 = {123, 456, 789};

        printArray(arr);
        printArray(arr2);
    }

第三章、泛型通配符和泛型限定

3.1)泛型的通配符

①? 表示泛型的通配符,表示集合中的任意数据类型,传递过来的表示什么数据类型,?就表示什么数据类型
②现在需要定义一个方法既可以接收ArrayList,又可以接收HashSet集合容器对象,只能使用它们的父类或接口类型来接收,这样就可以使用Collection接口类型接收。但是使用泛型不知道具体的数据类型,可以使用泛型的通配符 ? 来表示。

    //自定义方法:遍历打印Collection集合元素
    //这里书写Object不可以,因为泛型格式要求两侧必须类型一致,所以使用?通配符
    //Collection coll=new ArrayList()
    public static void printCollection(Collection<?> coll) {
        for (Object o : coll) {
            System.out.print(o + " ");
        }
        System.out.println();
    }
    //------------------------分割-------------------------------
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("def");
        HashSet<Integer> set = new HashSet<>();
        set.add(123);
        set.add(456);
        printCollection(list); //参数位置:Collection coll = new ArrayList()
        printCollection(set); //参数位置:Collection coll = new HashSet()
    }
}

3.2)泛型的限定

①上限通配符:<? extends T>,泛型中的参数必须是 T 或者 T 的子类
②下限通配符:<? super T>,泛型中的参数必须是 T 或者 T 的超类
③无限定通配符:,类型参数可以是任何类型

public class TestGeneric_2 {
    public static void main(String[] args) {
        //创建Person和其子类型的集合容器
        ArrayList<Person> pers = new ArrayList<>();
        ArrayList<Student> stus = new ArrayList<>();
        HashSet<Teacher> teas = new HashSet<>();

        //创建Animal和其子类型的集合容器
        ArrayList<Animal> ans = new ArrayList<>();
        ArrayList<Dog> dogs = new ArrayList<>();
        HashSet<Cat> cats = new HashSet<>();

        //遍历集合元素
        printCollection(pers);
        printCollection(stus);
        printCollection(teas);

//        printCollection(ans);//报错
//        printCollection(dogs);//报错
//        printCollection(cats);//报错
    }
//printCollection接受的参数必须是Person以及它的子类
    public static void printCollection(Collection<? extends Person> coll) {
        for (Person ps : coll) {
            System.out.print(ps + " ");
        }
        System.out.println();
    }
}

class Person{}
class Student extends Person{}
class Teacher extends Person{}

class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}

你可能感兴趣的:(Java进阶知识,java,开发语言)