@(工作笔记)
generic-record

[TOC]
泛型(Generics)
- 泛型是JDK1.5中一个最重要的特征。通过引入泛型,我们将获得编译时类型的安全和运行时更小地抛出ClassCastExceptions的可能。
- 在JDK1.5中,你可以声明一个集合将接收/返回的对象的类型
泛型之前
- 类别定义时的逻辑完全一样,只是里面成员变量的类型不同
- 如果需要多个相似的类,需要定义多个文件,不同的只是变量的类别,而逻辑是完全一样的
所谓泛型:就是变量类型的参数化。
类中使用
class Person<T, K, A, V> {
  private String   username;
  private T        friend;
  private K        country;
  private A[]      address;
  private Object[] versions;
  static {}
  {}
  public void getSimpleAddress() {
    // 没有办法消除警告,只能强转
    address = (A[]) new Objects[10];
  }
  public V getSimpleVersions(int index) {
    // 没有办法消除警告,只能强转,ArrayList也是这么实现的,参考get方法
    return (V) versions[index];
  }
  Person() {}
  public Person(String username, T friend) {
    this.username = username;
    this.friend   = friend;
  }
  @Override
  public String toString() {
    return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]")
        .add("username='" + username + "'")
        .add("friend=" + friend)
        .add("country=" + country)
        .add("address=" + Arrays.toString(address))
        .toString();
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public T getFriend() {
    return friend;
  }
  public void setFriend(T friend) {
    this.friend = friend;
  }
  public K getCountry() {
    return country;
  }
  public void setCountry(K country) {
    this.country = country;
  }
  public A[] getAddress() {
    return address;
  }
  public void setAddress(A[] address) {
    this.address = address;
  }
}
限制泛型可用类型
- 在定义泛型类别时,预设可以使用任何的类型来实例化泛型类型中的类型,但是如果想要限制使用泛型类别时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口·
- 当没有指定泛型继承的类型或接口时,默认使用T extends Object,所以默认情况下任何类型都可以作为参数传入
class Human {
}
class Person<F extends Human> {
  private F friend;
  public F getFriend() {
    return friend;
  }
  public void setFriend(F friend) {
    this.friend = friend;
  }
}
- 使用<?>或是<? extends SomeClass>的声明方式,意味着你只能通过该名称来取得所参考实例的信息,或者是移除某些信息,但不能增加它的信息,因为只知道当中放置的事SomeClass的子类,但不确定是什么类的实例,编译器不让你加入信息,理由是,如果可以键入信息的话,那么你就得记得取回的实例是什么类型,然后转换为原来的类型方可进行操作,这就失去了使用泛型的意义。
java 泛型中 T 和 问号(通配符)的区别
https://blog.csdn.net/jianghuxiaojin/article/details/51457773
通配符(?)
   当在赋值的时候,上面一节说赋值的都是为具体类型,当赋值的类型不确定的时候,我们用通配符(?)代替了:
List<?> unknownList;
List<? extends Number> unknownNumberList;
List<? super Integer> unknownBaseLineIntgerList;
在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能向其中添加元素, 因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL
Java 泛型 <? super T> 中 super 怎么 理解?与 extends 有何不同?
https://www.zhihu.com/question/20400700
Plate<? extends Fruit>
翻译成人话就是:一个能放水果以及一切是水果派生类的盘子。再直白点就是:啥水果都能放的盘子。这和我们人类的逻辑就比较接近了。Plate<? extends Fruit>和Plate
在这个体系中,上界通配符 “Plate<? extends Fruit>” 覆盖下图中蓝色的区域。

什么是下界?
相对应的,“下界通配符(Lower Bounds Wildcards)**”**:
Plate<? super Fruit>
表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子。Plate<? super Fruit>是Plate

下界<? super T>不影响往里存,但往外取只能放在Object对象里
使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。
Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());
//存入元素正常
p.set(new Fruit());
p.set(new Apple());
//读取出来的东西只能存放在Object类里。
Apple newFruit3=p.get();    //Error
Fruit newFruit1=p.get();    //Error
Object newFruit2=p.get();
因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。
定义含有泛型的方法
- 泛型定义在方法的修饰符和返回值类型之间 
- 格式 - 修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)) { -  方法体 - } 
- 含有泛型的方法,在调用方法的时候确定泛型的数据类型,传递什么类型参数,泛型就是什么类型 
 public static <T> void foo(T t) {
    dlog(t.getClass(), new Info("t.getClass()"));
 }
// public static <T> 是声明该方法是泛型函数