泛型:
要使代码能够应用于“某种具体的类型而不是一个具体的接口或类”。增加了泛型支持后的集合,完全可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误。增加泛型后的集合,可以让代码更加简洁,程序更加健壮(Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常)。除此之外,Java泛型还增强了枚举类、反射等方面的功能
当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。
例如,为Apple类定义构造器,其构造器名依然是Apple,而不是Apple!调用该构造器时却可以使用Apple的形式,当然应该为T形参传入实际的类型参数。Java 7提供了菱形语法,允许省略<>中的类型实参。
元组:
将一组对象直接打包存储于其中的一个单一的对象。这个容器对象允许读取其中的元素,但是不允许对其存放新的对象(这也称为:数据传送对象)
比如一个二维的元组:
public class TwoTuple{
public final A first;
public final B second;
public TwoTuple(A a, B b){ first = a; second = b;}
public String toStirng(){
return "("+first+","+second+")";
}
}
复制代码
构造一个堆栈类:
下面的例子使用了末端哨兵来判断堆栈何时为空,这个末端哨兵在构造LinkedStack时创建,然后每调用一次push()方法,就会创建一个Node对象,并将其链接到前一个Node对象。
public class LinkedStack{
private static class Node{
U item;
Node next;
Node() { item = null; next = null; }
Node(U item, Node next) {
this.item = item;
this.next = next;
}
boolean end(){ return item == null && next == null; }
}
private Node top = new Node(); // End sentinel
public void push(T item){
top = new Node(item, top);
}
public T pop(){
T result = top.item;
if(!top.end())
top = top.next;
return result;
}
public static void main(String[] args){
LinkedStack lss = new LinkedStack();
for(String s : "Phasers on stun!".split(" "))
lss.push(s);
String s;
while((s = lss.pop()) != null)
System.out.println(s);
}
} /* Output:
stun!
on
Phasers
*///:~
复制代码
泛型接口:
public interface Generator {T next();}
复制代码
用泛型接口实现生成Fibonacci数列:
public class Fibonacci implements Generator{
private int count=0;
public Integer next(){return fib(count++);}
private int fib(int n){
if(n<2) return 1;
return fib(n-2)+fib(n-1);
}
public static void main(String[] args){
Fibonacci gen = new Fibonacci();
for(int i=0;i<18;i++){
System.out.println(gen.next()+" ");
}
}
}
/* output
1 1 2 3 5 8 13 21 34 55 89 144 233 377 ...复制代码
泛型方法:
泛型方法使得该方法能够独立于类而产生变化。无论何时,只要你能做到,就应该尽量使用泛型方法。也就是说如果使用泛型方法可以取代整个类泛型化,那么就应该使用泛型方法,因为他可以使得事情更清楚明白。
定义泛型方法: 修饰符 返回参数 方法名(接收参数){}
public class MyUtils{
// 标明是一个泛型方法。
public static void copy(Collection dest , Collection extends T> src){..
.} //①
public static T copy(Collection super T> dest , Collection src){.
..} //②
}
泛型中的类型转换,在未知类型时不能进行强转:用下面方式应该先判断
List>[] lsa=new ArrayList>[10];
Object[] oa=(Object[]) lsa;
List li=new ArrayList();
li.add(new Integer(3));
oa[1]=li;
Object target=lsa[1].get(0);
if (target instanceof String){
// 下面代码安全了
String s=(String) target;
}
复制代码
使用泛型构建一个Set的使用工具(交集,差集):
import java.util.*;
public class Sets{
// 将两个set合并为一个set
public static Set union(Set a, Set b){
Set result = new HashSet(a);
result.addAll(b);
return result;
}
// 返回共有的交集
public static
Set intersection(Set a, Set b){
Set result = new HashSet(a);
result.retainAll(b);
return result;
}
// 从superset移除subset包含的元素
// Subtract subset from superset:
public static Set
difference(Set superset, Set subset){
Set result = new HashSet(superset);
result.removeAll(subset);
return result;
}
// 返回交集之外的所有元素
// Reflexive--everything not in the intersection:
public static Set complement(Set a, Set b){
return difference(union(a, b), intersection(a, b));
}
} ///:~
复制代码
泛型擦除:
在泛型代码内部,无法获取任何有关泛型参数类型的信息。java使用泛型擦除,比如List 和List 在运行时都是相同的类型,均被擦除为"原生"类型即List. 泛型擦除就是被擦除为父类。
泛型的面试题
Q&A Java中的泛型是什么 ? 使用泛型的好处是什么?
在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入 集合中,避免了在运行时出现ClassCastException
Q&A Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。
Q&A什么是泛型中的限定通配符和非限定通配符 ?
限定通配符对类型进行了限制。有两种限定通配符,一种是它通过确保类型必须是T的子类来设定类型的上界,另一种是它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面表 示了非限定通配符,因为>可以用任意类型来替代
Q&A List extends T> 上界 和List super T> 下界 之间有什么区别 ?
上界的list只能get不能add,下届的list只能add不能get
编译器可以支持像上转型,不支持像下转型。
Q&A 你可以把List传递给一个接受List参数的方法吗?
因为List可以存储任何类型的对象包括String, Integer等等,而List却只能用来存储Strings。
List objectList;
List stringList;
objectList = stringList; //compilation error incompatible types
public static void main(String[] args){
List stringList = new ArrayList<>();
List objectList = new ArrayList<>();
stringList.add("add");
stringList.add("123");
objectList.add("123");
objectList.add("234");
// 让objectList转为stringList,编译错误stringList必须是接收的List List之间不能转换。
// stringList= objectList; // 编译错误
// objectList=stringList; // 编译错误
List> list = new ArrayList<>();
list = stringList;
System.out.println(list);
list = objectList;
System.out.println(list);
// list.add("sss"); // 编译器不允许这样使用
}
复制代码