SE知识整理——泛型

泛型

一、什么是泛型?

Generic

设计原则: 只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.

即把运行期异常提升至编译器异常。

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型 。

  • ArrayList<E>中的E称为类型参数变量
  • ArrayList<Integer>中的Integer称为实际类型参数
  • 整个称为ArrayList<E>泛型类型
  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

1581769713360

二、为什么要使用泛型?

我们可以用Object表示任意类型,但是需要向下强转,那自然就会不太安全。

  • 比如:Collection,Map集合存储的元素类型是无限制的,当向下转型就可能出现异常。
private static void show01(){
        ArrayList list = new ArrayList();
        list.add("abc");
        list.add(123123);
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            Object o = iterator.next();
            System.out.println(o);

            //想要使用String类的length获取长度....--->无法使用需要向下转型
            String s = (String) o;
            System.out.println(s.length());
            // ---> 出现异常。java.lang.Integer cannot be cast to java.lang.String
        }
    }

使用泛型的好处:

  • 代码更加简洁【避免强制转换】
  • 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
  • 可读性和稳定性【在编写集合的时候,就限定了类型】

使用泛型明确对象的类型,则可以使用foreach遍历

三、泛型基础

3.1 泛型类

把泛型定义在类上,用户使用该类的时候,才把类型明确下来 。

DEMO:

public class GenericDemo<E> {
    private E name;

    public E getName() {
        return name;
    }

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

class TestDemo(){
    //不是用泛型,创建的是Obj对象。
    GenericDemo g1 = new GenericDemo();
    g1.setName("liuyang");
    Object o = g1.getName(); // object 
    
    //使用泛型
     GenericDemo<Integer> gc = new GenericDemo<>();
     gc.setName(1);
     System.out.println(gc.getName()); //Integer

     GenericDemo<String> gc2 = new GenericDemo<>();
     gc2.setName("1111111111");
     System.out.println(gc2.getName()); //String
}

3.2 泛型方法

格式:

修饰符 <代表泛型的变量> 返回值类型 方法名(参数) {
    
}

调用该方法,才确定类型。

//
public <T> void show01(T t){
    sout(t);
}

//静态泛型方法
public static <T> void show02(T t){
    sout(t);
}

public static void main(String[] args){
    show01(10);
    show01("10");
    
    show02(10);
    show02("10");
}

3.3 泛型接口的两种实现方式

GenericInterface.java 泛型接口

  • 子类明确泛型类的类型参数变量 :实现类1
  • 子类不明确泛型类的类型参数变量 :实现类2
public interface GenericInterface<T> {
    public abstract void method(T t);
}

实现类1:

public class GenericInterfaceImpl1 implements GenericInterface<String>{
    @Override
    public void method(String s) {
        System.out.println(s);
    }
}
class TestGenericInterfaceImpl1{
    public static void main(String[] args) {
        GenericInterface gi = new GenericInterfaceImpl1();
        gi.method("qweqwe");
    }
}

实现类2:实现类的类型跟着接口走。

public class GenericInterfaceImpl2<T> implements GenericInterface<T>{
    @Override
    public void method(T t) {
        System.out.println(t);
    }
}

class TestGenericInterfaceImpl2{
    public static void main(String[] args) {
        GenericInterface<String> gi2 = new GenericInterfaceImpl2();
        gi2.method("qweqwe");
    }
}

重要

  • 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
  • 类上声明的泛形只对非静态成员有效

3.4 泛型通配符

当使用泛型类/接口时,类型不确定可以使用通配符<?>

一旦使用了通配符,则只能使用Object类的方法,集合元素自身的方法无法使用。

使用场景:不知道使用什么类型来接收的时候。

只能接受数据,不能往集合存储数据。

DEMO1

public class Demo05Generic {
    public static void main(String[] args) {

        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(2);
        list01.add(3);

        ArrayList<String> list02 = new ArrayList<>();
        list02.add("1");
        list02.add("2");
        list02.add("3");

        printList(list01);
        printList(list02);

    }

    public static void printList(ArrayList list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

未学泛型前,采用上述代码。但这样做会编译警告,不优雅。

改进:code2 使用?通配符

 public static void printList(ArrayList<?> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

有些人可能会这样写,但是错误的。

 public static void printList(ArrayList<Object> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

上面的代码:ArrayList<Object>有点想当然了,泛型中不存在继承的关系,也就是说ArrayList<Object>ArrayList<String>毫无关系。

现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。

  • 泛型通配符的高级使用(为了看懂源码
    • 上限限定:? extends E 使用的泛型只能是E类型的子类或者本身
    • 下限限定:? super E 使用的泛型只能是E类型的父类或者本身

实例:

 public class Demo06Generic {
    public static void main(String[] args) {
        Collection<Integer> l1 = new ArrayList<>();
        Collection<String> l2 = new ArrayList<>();
        Collection<Number> l3 = new ArrayList<>();
        Collection<Object> l4 = new ArrayList<>();

        //Integer Number Object
        //String Object
        
        getElement1(l1);
        getElement1(l2);//error
        getElement1(l3);
        getElement1(l4);//error

        getElement2(l1);//error
        getElement2(l2);//error
        getElement2(l3);
        getElement2(l4);
    }

    public static void getElement1(Collection<? extends Number> collection){}
    public static void getElement2(Collection<? super Number> collection){}

}

四、其他关于泛型的内容(以后看

泛型擦除/泛型数组/泛型应用

原文地址:https://www.cnblogs.com/Lysz1996/p/12492983.html