Java随笔-生活工作点滴

【Java 笔记】Java 泛型相关整理

2019-05-23  本文已影响52人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

1. 概述

public static void main(String[] args) {
        List<String> stringList = new ArrayList<String>();
        List<Integer> integerList = new ArrayList<Integer>();

        Class classStringArrayList = stringList.getClass();
        Class classIntegerArrayList = integerList.getClass();

        System.out.println(classStringArrayList.getClass() == classIntegerArrayList.getClass());
}
// print true

2. 泛型的使用

2.1 泛型类

public class Pair<T> {
    private T field;
}

2.2 泛型接口

public interface Generator<T> {
    public T next();
}
/**
 * 即:class FruitGenerator<T> implements Generator<T> {
 * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}
/**
 * 传入泛型实参时:
 * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口 Generator<T>
 * 但是我们可以为 T 传入无数个实参,形成无数种类型的 Generator 接口。
 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
 * 即:Generator<T>,public T next(); 中的的 T 都要替换成传入的 String 类型。
 */
public class FruitGenerator implements Generator<String> {
 
    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
 
    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

2.3 泛型方法

public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
  IllegalAccessException{
        T instance = tClass.newInstance();
        return instance;
}
public class StaticGenerator<T> {
    ....
    /**
     * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
     * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
     * 如:public static void show(T t){..},此时编译器会提示错误信息:
          "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show(T t) {
    }
}

泛型方法和可变参数

public <T> void printMsg( T... args){
    for(T t : args){
         System.out.println(t);
    }
}

2.4 类型通配符

// JDK 1.7 之前
Map<String, String> map = new HashMap<String, String>(); 
// JDK 1.7 类型推断
Map<String, String> map = new HashMap<>();

2.5 泛型上下边界

上限(extends)子类型通配符

上限(extends)范围
public class Plate <? extends Fruit> {
}

下限(super)超类型通配符

下限(super)范围
public class Plate <? super Fruit> {
}

2.6 泛型数组

List<String>[] ls = new ArrayList<String>[10];  
List<?>[] ls = new ArrayList<?>[10];  
List<String>[] ls = new ArrayList[10];
List<String>[] lsa = new List<String>[10]; // Not really allowed.
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Unsound, but passes run time store check
String s = lsa[1].get(0); // Run-time error: ClassCastException.
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.    
Object o = lsa;    
Object[] oa = (Object[]) o;    
List<Integer> li = new ArrayList<Integer>();    
li.add(new Integer(3));    
oa[1] = li; // Correct.    
Integer i = (Integer) lsa[1].get(0); // OK

2.7 泛型的约束和限制

泛型的类型参数只能是类类型,不能是简单类型

类型检查不可使用泛型

if(aaa instanceof Pair<String>){}//error
Pair<String> p = (Pair<String>) a;//warn
Pair<String> p;
Pair<Integer> i;
i.getClass()==p.getClass();//true
public class Test {

    Class<?> aClass;

    public Test(Class<?> aClass) {
        this.aClass = aClass;
    }

    public boolean isInstance(Object object) {
        return aClass.isInstance(object);
    }

    public static void main(String[] args) {
        Test test = new Test(A.class);
        System.out.println(test.isInstance(new A()));
        System.out.println(test.isInstance(new B()));
    }

    public static class A {
    }

    public static class B {
    }
}
/** print
true
false
**/

不能实例化泛型对象

T t= new T();//error
T.class.newInstance();//error
T.class;//error
public void sayHi(Class<T> c){
  T t=null;
  try {
    t=c.newInstance();
  } catch (Exception e) {
    e.printStackTrace();
  }
  System.out.println("Hi "+t);
}

不能在泛型类的静态域中使用泛型类型

public class Singleton<T>{
    private static T singleton; //error
    public static T getInstance(){} //error
    public static void print(T t){} //error
}
public static <T> T getInstance(){return null;} //ok
public static <T> void print(T t){} //ok

不能捕获泛型类型的对象

public class GenericThrowable<T> extends Throwable{
  //The generic class GenericThrowable<T> may not subclass java.lang.Throwable
}
@Test
public void testGenericThrowable(){
  GenericThrowable<RuntimeException> obj=new GenericThrowable<RuntimeException>();
  obj.doWork(new RuntimeException("What did you do?"));
}
public static class GenericThrowable<T extends Throwable>{
  public void doWork(T t) throws T{
    try{
      Reader reader=new FileReader("notfound.txt");
      //这里应该是checked异常
    }catch(Throwable cause){
      t.initCause(cause);
      throw t;
    }
  }
}

2.8 泛型嵌套

Student<String> student = new Student<String>();
student.setScore("优秀");
System.out.println(student.getScore());
    
//泛型嵌套
School<Student<String>> school = new School<Student<String>>();
school.setStu(student);
    
String s = school.getStu().getScore(); //从外向里取
System.out.println(s);

// hashmap 使用了泛型的嵌套
Map<String, String> map =  new HashMap<String,String>();
map.put("a", "张三");
map.put("b", "李四");
Set<Entry<String, String>> set = map.entrySet();
for (Entry<String, String> entry : set) {
     System.out.println(entry.getKey() + ":" + entry.getValue());
}

3. 泛型擦除

3.1 擦除方式

class Parent<T extends SQLException>{
    public void test() throws T{}  
}

class Son extends Parent<BatchUpdateException>{
    @Override
    public void test() throws BatchUpdateException{} //这里必须与参数类型保持一致,否则编译不通过。
}
class Super<SQLException>{
    public void test() throws SQLException{}
}
class Parent<T>{
    T test(){}
}
class Son extends Parent<String>{
    @Override
    protected String test(){}  //protected 拥有比 package 更高的访问权限,可以被同一包内的类访问
}
class Super{
    Object test(){}
}

3.2 擦除原理

public class Test {

    private Object obj;

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.setObj("test");
        String testString = (String) test.getObj();
    }
}
public class Test
{
  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest;

  public java.lang.Object getObj();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field obj:Ljava/lang/Object;
         4: areturn
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest;

  public void setObj(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #2                  // Field obj:Ljava/lang/Object;
         5: return
      LineNumberTable:
        line 10: 0
        line 11: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   LTest;
            0       6     1   obj   Ljava/lang/Object;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #3                  // class Test
         3: dup
         4: invokespecial #4                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #5                  // String test
        11: invokevirtual #6                  // Method setObj:(Ljava/lang/Object;)V
        14: aload_1
        15: invokevirtual #7                  // Method getObj:()Ljava/lang/Object;
        18: checkcast     #8                  // class java/lang/String
        21: astore_2
        22: return
}
public class Test<T> {

    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }

    public static void main(String[] args) {
        Test<String> test = new Test<String>();
        test.setObj("test");
        String string = test.getObj();
    }
}
public class Test<T extends java.lang.Object> extends java.lang.Object
{
  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest<TT;>;

  public T getObj();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field obj:Ljava/lang/Object; 运行期为 Object 类型
         4: areturn
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest<TT;>;
    Signature: #25                          // ()TT;

  public void setObj(T);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #2                  // Field obj:Ljava/lang/Object;
         5: return
      LineNumberTable:
        line 10: 0
        line 11: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   LTest;
            0       6     1   obj   Ljava/lang/Object;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   LTest<TT;>;
            0       6     1   obj   TT;
    Signature: #28                          // (TT;)V

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #3                  // class Test
         3: dup
         4: invokespecial #4                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #5                  // String test
        11: invokevirtual #6                  // Method setObj:(Ljava/lang/Object;)V
        14: aload_1
        15: invokevirtual #7                  // Method getObj:()Ljava/lang/Object;
        18: checkcast     #8                  // class java/lang/String 类型转换为编译器自动添加
        21: astore_2
        22: return
}
Signature: #37                          // <T:Ljava/lang/Object;>Ljava/lang/Object;

3.3 擦除残留

Signature: #37                          // <T:Ljava/lang/Object;>Ljava/lang/Object;

3.4 擦除冲突

重载与重写

public class Parent {

    private Object obj;

    public void setName(Object name) {
        System.out.println("Parent:" + name);
    }

    public Object getName() {
        return obj;
    }
}
public class Son extends Parent {
    private String obj;

    public void setName(String name) {
        System.out.println("Son:" + name);
    }

    public String getName() {
        return obj;
    }

    public static void main(String[] args) {
        Son son = new Son();
        son.setName("abc");
        son.setName(new Object());
    }
}
/** print
Son:abc
Parent:java.lang.Object@1b6d3586
** /
public class Parent<T> {

    public void setName(T name) {
        System.out.println("Parent:" + name);
    }
}
public class Parent {
        public void setName(Object name) {
            System.out.println("Parent:" + name);
        }
}
public class Son extends Parent<String> {
        public void setName(String name) {
              System.out.println("Son:" + name);
        }

        public static void main(String[] args) {
              Son son = new Son();
              son.setName("abc");
              son.setName(new Object());//The method setName(String) in the type Son is not applicable for the arguments (Object)
        }
}
public void setName(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #5                  // String Son:
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_1
        16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
public void setName(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #15                 // class java/lang/String
         5: invokevirtual #13                 // Method setName:(Ljava/lang/String;)V
         8: return
public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #9                  // Field obj:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LSon;
public java.lang.Object getName();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokevirtual #14                 // Method getName:()Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LSon;
public boolean equals(T value){
        return (obj.equals(value));
}

继承泛型的参数化

import java.util.Comparator;
public class Parent implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        return 0;
    }
}

import java.util.Comparator;
public class Son extends Parent implements Comparator {
}
import java.util.Comparator;
public class Parent implements Comparator<Parent> {

    @Override
    public int compare(Parent o1, Parent o2) {
        return 0;
    }
}

import java.util.Comparator;
public class Son extends Parent implements Comparator<Son> {
}

3.5 边界类型的协变性与逆变性

List<? extends Parent> list = new ArrayList<Son>();  //协变
List<? super Son> list2 = new ArrayList<Parent>();   //逆变
List<Parent> list = new ArrayList<Son>();
List<Object> list2 = new ArrayList<Parent>(); 

里氏替换原则

协变与逆变的定义

class Parent {
    Number method(Number n) { ... }
}

class Son extends Parent {
    @Override 
    Number method(Number n) { ... }
}
class Parent {
    Number method(Number n) { ... }
}

class Son extends Parent {
    @Override 
    Integer method(Number n) { ... }
}

PECS 原则

4. 泛型与继承

public class Son extends Parent<String> {}
public class Son<T> extends Parent<T> {}
Pair<Son> s = new Pair<>();
Pair<Parent> p = s; //error
public class Parent<T> {

    private T name;

    public T getName() {
        return name;
    }

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

    public static void main(String[] args) {
        Parent<String> p1 = new Parent<>();
        p1.setName("abc");
        System.out.println(p1.getName());
        Parent p2 = p1;
        p2.setName(new File("1.txt")); //error
        System.out.println(p2.getName());
    }
}
/** print
abc
1.txt
**/

5. 泛型与反射

参考资料

http://www.sohu.com/a/245549100_796914
https://www.imooc.com/article/18159
https://blog.csdn.net/tyrroo/article/details/80930938
https://blog.csdn.net/wang__qin/article/details/81415223
https://segmentfault.com/a/1190000014824002
https://www.runoob.com/java/java-generics.html
https://www.cnblogs.com/coprince/p/8603492.html
https://www.cnblogs.com/lwbqqyumidi/p/3837629.html

上一篇 下一篇

猜你喜欢

热点阅读