泛型

泛型
泛型,即“参数化类型”。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

为什么要使用泛型?
编程的时候,能在编译时发现并修改错误最好,等上线运行时报错才解决,则属于生产事故,且找到bug的位置需要花费更多的时间和精力。泛型是java1.5以后出的内容,运用泛型,指定集合中的对象类型,你可以在编译时发现类型不匹配的错误,并且取数据时不需要手动强转类型。

javaSE 7 以后构造函数可以省略泛型

ArrayList<String> list = new ArrayList<>();
但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数。常见的如: 
E - Element (在集合中使用,因为集合中存放的是元素)

T - Type(Java 类)

K - Key(键)

V - Value(值)

N - Number(数值类型)

? - 通配符

S、U、V  - 2nd、3rd、4th types

泛型方法:

public class ArrayAlg {

    public static <T> T getMiddle(T... a){
        return a[a.length/2];
    }
}

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(ArrayAlg.getMiddle("one", "two", "three"));
        System.out.println(ArrayAlg.getMiddle(3.14, 3, 10));
        System.out.println(ArrayAlg.getMiddle("3", 3.14, 3));
    }
}

结果:
two
3
3.14

ArrayAlg.class反编译

public class Test
{
  public static void main(String[] args)
  {
    System.out.println((String)ArrayAlg.getMiddle(new String[] { "one", "two", "three" }));
    System.out.println(ArrayAlg.getMiddle(new Number[] { Double.valueOf(3.14D), Integer.valueOf(3), Integer.valueOf(10) }));
    System.out.println(ArrayAlg.getMiddle(new Serializable[] { "3", Double.valueOf(3.14D), Integer.valueOf(3) }));
  }
}

类型变量的限定
如果泛型是T的话,那么T可能是任意对象,怎么才能确定T所属类有compareTp方法呢?所以要将T限定为实现了Comparable接口的类。

public static <T extends Comparable> T min(T[] a){
        if(a == null ||a.length == 0) return null;
        T smallest = a[0];
        for(int i = 1; i < a.length; i++){
            if(smallest.compareTo(a[i]) > 0) smallest = a[i];
        }
        return smallest;
    }

一个类型变量或通配符可以有多个限定
T extends Comparable & Serializable
用“&”分隔开。

类型擦除

Pair<T>的原始类型如下:
public class Pair {

    private Object first;
    private Object second;

    public Pair() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Pair(Object first, Object second) {
        super();
        this.first = first;
        this.second = second;
    }

    public Object getFirst() {
        return first;
    }

    public void setFirst(Object first) {
        this.first = first;
    }

    public Object getSecond() {
        return second;
    }

    public void setSecond(Object second) {
        this.second = second;
    }

}

原始类型(raw type)就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型。无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除(crased),并使用其限定类型(无限定的变量用Object)替换。

通配符:
通配符的出现是为了指定泛型中的类型范围。
1. <?> 被称作无限定的通配符。
2. <? extends T> 被称作有上限的通配符。它表示T以及T的子类, 类型最高是T
3. <? super T> 被称作有下限的通配符。它表示T以及T的超类,类型最高可到Object ,最低是T

无限定通配符

public void testWildCards(Collection<?> collection){
}
Base是Sub的父类
<? extends T>
<?> 代表着类型未知,但是我们的确需要对于类型的描述再精确一点,我们希望在一个范围内确定类别,
比如类型 A 及 类型 A 的子类都可以。

public void testSub(Collection<? extends Base> para){

}
.
.
.
para.add(new Sub());
para.add(new Base());

上面代码中,para 这个 Collection 接受 Base 及 Base 的子类的类型。

<? super T>
public void testSuper(Collection<? super Sub> para){
    para.add(new Sub());//编译通过
    para.add(new Base());//编译不通过
}
.
.
.
我们现在可以下结论了,在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T> 则会
被转译成普通的 Object 类型,如果指定了上限如 <T extends String> 则类型参数就被替换成类型上限。
List<String>、List<T> 擦除后的类型为 ListList<String>[]、List<T>[] 擦除后的类型为 List[]。
List<? extends E>、List<? super E> 擦除后的类型为 List<E>。
List<T extends Serialzable & Cloneable> 擦除后类型为 List<Serializable>。

翻译泛型表达式

Pair<Employee> buddies = "...";
Employee buddy= buddies.getFirst();

擦除getFirst()的返回类型后返回Object类型,编译器自动插入Employee的强制类型转换,将返回Object的强制转换类型。

翻译泛型方法

class DateInterval extends Pair<LocalDate>{

    public void setSecond(LocalDate second){

    }
}
//擦除后
class DateInterval extends Pair{

    public void setSecond(LocalDate second){

    }
}

桥方法:https://blog.csdn.net/lcb1992/article/details/53116763

**虚拟机中没有泛型,只有普通的方法
所以得类型参数都用它们的限定类型替换
桥方法被合成来保持多态
为保持类型安全性,必要时插入强制类型转换**

Varargs警告

public static <T> void addAll(Collection<T> coll, T... ts){

    for(t : ts) coll.add(t);
}
.
.
.
Collection<Pair<String>> table = ...;
Pair<String> pair1 = ...;
Pair<String> pair2 = ...;
addAll(table, pair1, pair2);

addAll方法的 T... ts参数是可变的,会自动转换为一个数组,泛型数组是不允许存在的,但是对于这种情况,规则有所放松,你会得到一个警告

Pair.java

public class Pair<T> 
{
   private T first;
   private T second;

   public Pair() { first = null; second = null; }
   public Pair(T first, T second) { this.first = first;  this.second = second; }

   public T getFirst() { return first; }
   public T getSecond() { return second; }

   public void setFirst(T newValue) { first = newValue; }
   public void setSecond(T newValue) { second = newValue; }
}

PairTest.java

public class PairTest
{
   public static void main(String[] args)
   {
      Manager ceo = new Manager("Tom", 800000, 2003, 12, 15);
      Manager cfo = new Manager("Harray", 600000, 2003, 12, 15);
      Pair<Manager> buddies = new Pair<>(ceo, cfo);      
      printBuddies(buddies);

      ceo.setBonus(1000000);
      cfo.setBonus(500000);
      Manager[] managers = { ceo, cfo };

      Pair<Employee> result = new Pair<>();
      minmaxBonus(managers, result);
      System.out.println("first: " + result.getFirst().getName() 
         + ", second: " + result.getSecond().getName());
      maxminBonus(managers, result);
      System.out.println("first: " + result.getFirst().getName() 
         + ", second: " + result.getSecond().getName());
   }

   public static void printBuddies(Pair<? extends Employee> p)
   {
      Employee first = p.getFirst();
      Employee second = p.getSecond();
      System.out.println(p.getClass().getName() + " --- " +p.getFirst().getClass().getName()+"---"+p.getSecond().getClass().getName());
   }

   public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
   {
      if (a.length == 0) return;
      Manager min = a[0];
      Manager max = a[0];
      for (int i = 1; i < a.length; i++)
      {
         if (min.getBonus() > a[i].getBonus()) min = a[i];
         if (max.getBonus() < a[i].getBonus()) max = a[i];
      }
      result.setFirst(min);
      result.setSecond(max);
   }

   public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
   {
      minmaxBonus(a, result);
      PairAlg.swapHelper(result); // OK--swapHelper captures wildcard type
   }
   // Can't write public static <T super manager> ...
}

class PairAlg
{
   public static boolean hasNulls(Pair<?> p)
   {
      return p.getFirst() == null || p.getSecond() == null;
   }

   public static void swap(Pair<?> p) { swapHelper(p); }

   public static <T> void swapHelper(Pair<T> p)
   {
      T t = p.getFirst();
      p.setFirst(p.getSecond());
      p.setSecond(t);
   }
}


反编译
PairTest.java

public class PairTest
{
    public static void main(final String[] args) {
        final Manager ceo = new Manager("Tom", 800000.0, 2003, 12, 15);
        final Manager cfo = new Manager("Harray", 600000.0, 2003, 12, 15);
        //如果Pai类的泛型固定是<Employee>的话,ceo和cfo就不用转型,因为下面已经声明了泛型是Manager
        final Pair<Manager> buddies = (Pair<Manager>)new Pair((Object)ceo, (Object)cfo); 
        printBuddies((Pair<? extends Employee>)buddies);
        ceo.setBonus(1000000.0);
        cfo.setBonus(500000.0);
        final Manager[] managers = { ceo, cfo };
        final Pair<Employee> result = (Pair<Employee>)new Pair();
        minmaxBonus(managers, (Pair<? super Manager>)result);
        System.out.println("first: " + ((Employee)result.getFirst()).getName() + ", second: " + ((Employee)result.getSecond()).getName());
        maxminBonus(managers, (Pair<? super Manager>)result);
        System.out.println("first: " + ((Employee)result.getFirst()).getName() + ", second: " + ((Employee)result.getSecond()).getName());
         //如果Pai类的泛型固定是<Employee>的话,ceo和cfo就不用转型,因为下面已经声明了泛型是Manager
        final Pair<Manager> p1 = (Pair<Manager>)new Pair((Object)ceo, (Object)cfo);
        //如果Pai类的泛型固定是<Employee>的话需要转型(Employee)ceo,(Employee)cfo,因为下面没有声明泛型是Manager
        final Pair p2 = new Pair((Object)ceo, (Object)cfo);
        PairAlg.hasNulls(p1);
        PairAlg.swapHelper(p1);
        PairAlg.hasNulls((Pair<?>)p2);
        PairAlg.swapHelper((com.Pair<Object>)p2);
    }

    public static void printBuddies(final Pair<? extends Employee> p) {
        final Employee first = (Employee)p.getFirst();
        final Employee second = (Employee)p.getSecond();
        System.out.println(String.valueOf(p.getClass().getName()) + " --- " + ((Employee)p.getFirst()).getClass().getName() + "---" + ((Employee)p.getSecond()).getClass().getName());
    }

    public static void minmaxBonus(final Manager[] a, final Pair<? super Manager> result) {
        if (a.length == 0) {
            return;
        }
        Manager min = a[0];
        Manager max = a[0];
        for (int i = 1; i < a.length; ++i) {
            if (min.getBonus() > a[i].getBonus()) {
                min = a[i];
            }
            if (max.getBonus() < a[i].getBonus()) {
                max = a[i];
            }
        }
        result.setFirst((Object)min);
        result.setSecond((Object)max);
    }

    public static void maxminBonus(final Manager[] a, final Pair<? super Manager> result) {
        minmaxBonus(a, result);
        PairAlg.swapHelper(result);
    }
}

class PairAlg
{
    public static boolean hasNulls(final Pair<?> p) {
        return p.getFirst() == null || p.getSecond() == null;
    }

    public static void swap(final Pair<?> p) {
        swapHelper(p);
    }

    public static <T> void swapHelper(final Pair<T> p) {
        final Field[] fields = p.getClass().getDeclaredFields();
        Field[] array;
        for (int length = (array = fields).length, i = 0; i < length; ++i) {
            final Field f = array[i];
            //System.out.println(f.getType().getName()); //p的成员变量都是Object类型
        }
        //p是Pair类型 t是Manager类型
        final T t = p.getFirst();
        p.setFirst(p.getSecond());
        p.setSecond(t);
    }
}

/*
com.Pair --- com.Manager---com.Manager
first: Harray, second: Tom
first: Tom, second: Harray
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值