泛型实现了参数化类型的概念,使代码应用于 多种类型。参数化类型,顾名思义就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
- 1 简单泛型
class Automobile{
public void print(){
System.out.println("print()");
}
}
public class Holder<T> {
private T t;
public Holder(T t){
this.t = t;
}
public T getT(){
return t;
}
public static void main(String[] args) {
Holder<Automobile> holder = new Holder<Automobile>(new Automobile());
holder.getT().print();
}
}
当你创建Holder时,必须指明想持有什么类型的对象,将其置于尖括号内,那么就只能在Holder中存入该类型的对象了,并且在你从Holder中取出他持有的对象时,自动的就是正确的类型了。这就是java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切的细节
- 2.一个堆栈类
public class LinkedList<T> {
private static class Node<U>{
U item;
Node<U> next;
Node(){
item = null;
next = null;
}
Node(U item, Node<U> next){
this.item = item;
this.next = next;
}
boolean end(){return item == null & next == null;}
}
private Node<T> top = new Node<T>();
public void push(T item){
top = new Node<T>(item, top);
}
public T pop(){
T result = top.item;
if(!top.end()){
top = top.next;
}
return result;
}
public static void main(String[] args) {
LinkedList<String> lss = new LinkedList<String>();
for(String str : "zhou li bin".split(" ")){
lss.push(str);
}
String ss;
while((ss = lss.pop())!=null){
System.out.println(ss);
}
}
}
- 3.自定义泛型接口
泛型 也可以用作与接口
public interface Generator<T> {
T next();
}
方法next()的返回类型是参数化 的T,接口使用泛型与类使用泛型没啥区别
import java.util.Iterator;
import java.util.Random;
class Coffee{
private static long counter = 0;
private final long id = counter++;
public String toString(){
return this.getClass().getSimpleName()+" "+ id;
}
}
class Latte extends Coffee{}
class Mocha extends Coffee{}
class Cappuccino extends Coffee{}
class Breve extends Coffee{}
public class CoffeeGenerator implements Generator<Coffee>,Iterable<Coffee>{
private Class[] types = {Latte.class,Mocha.class,Cappuccino.class,Breve.class};
private static Random rand = new Random(47);
private int size = 0;
public CoffeeGenerator(){}
public CoffeeGenerator(int sz){
this.size = sz;
}
public static void main(String[] args) {
CoffeeGenerator co = new CoffeeGenerator();
for(int i=0;i<4;i++){
System.out.println(co.next());
}
for(Coffee c : new CoffeeGenerator(4)){
System.out.println(c);
}
}
@Override
public Coffee next() {
try{
return (Coffee) types[rand.nextInt(types.length)].newInstance();
}catch(Exception ex){
throw new RuntimeException();
}
}
class CoffeeIterator implements Iterator<Coffee>{
int count = size;
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return count > 0;
}
@Override
public Coffee next() {
// TODO Auto-generated method stub
count --;
return CoffeeGenerator.this.next();
}
@Override
public void remove() {
}
}
@Override
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
}
一个实现类,实现Generator<Coffee>接口,能够随机生成不同类型的Coffee对象
参数化 的Generator接口确保next()返回值是参数的类型,CoffeeIterator同时还实现了Iterable接口,所有他可以在循环中使用,这就是第二个构造器 的应用
- 4.泛型斐波那契的应用
public class Fibonacci implements Generator<Integer>{
private int count = 0;
public Fibonacci(int n){
this.count = n;
}
public Fibonacci(){}
@Override
public Integer next() {
return fib(count++);
}
public int fib(int n){
if(n < 2) return 1;
return fib(n-2)+fib(n-1);
}
public static void main(String[] args) {
Fibonacci fib = new Fibonacci();
for(int i=0;i<18;i++){
System.out.print(fib.next()+" ");
}
}
}
这个例子引出了泛型的一个局限性:基本类型无法作为泛型参数,所以Int不行,使用Integer,但java提供了自动打包和自动拆包的功能,可以在基本类型与包装类型之间转换
- 5.泛型方法
泛型方法适当方法可以独立于类而产生变化
要定义泛型方法,只需将泛型参数列表置于返回值之前
import java.util.Arrays;
public class GenericMethods {
public <T> void f(T t){
System.out.println(t.getClass().getSimpleName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("hello");
gm.f(10);
gm.f(Arrays.asList("zhou li bin".split(" ")));
}
}
注意:当使用泛型类时,必须在创建对象的时候指定类型参数的值,而是用泛型方法的时候,不必指明参数的类型,因为编译器会帮我们找出具体的类型,这称为类型参数判断
- 6.一个通用的Generator
public interface Generator<T> {
T next();
}
public class BasicGenerator<T> implements Generator<T>{
private Class<T> typpe;
public BasicGenerator(Class<T> type){
this.typpe = type;
}
@Override
public T next() {
try {
return typpe.newInstance();
} catch (Exception e) {
throw new RuntimeException();
}
}
public static <T> Generator<T> create(Class<T> t){
return new BasicGenerator<>(t);
}
}
这个类提供了一个基本的实现,用以生成某个类的对象。这个类必须具备两个条件:(1)它必须声明为public (2)必须具有默认的构造器
- 7.匿名内部类
泛型还可以用于内部类以及匿名内部类
public interface Generator<T> {
T next();
}
public class Customer {
private static long counter = 0;
private final long id = counter++;
private Customer(){}
public String toString(){
return "Customer\t"+id;
}
public static Generator<Customer> Generator(){
return new Generator<Customer>() {
@Override
public Customer next() {
return new Customer();
}
};
}
}
Customer只有private的构造器,这可以强制你必须使用Generator对象
- 8.擦除的神秘之处
尽管可以声明ArrayList.class,但是不能声明ArrayList<Integer>.class
import java.util.ArrayList;
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
class Frob{}
class Fnorkle{}
class Quark<Q>{}
public class LostInformation {
public static void main(String[] args) {
ArrayList<Frob> list = new ArrayList<Frob>();
Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
}
}
因此残酷的现实是:在泛型代码内部,无法获得任何有关泛型参数的信息
java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你惟一知道的就是你在使用一个对象。因此,List<String>和List<Integer>在运行时事实上都是相同的类型
- 9.擦除的补偿
擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都无法工作。
public class ClassTypeCapture<T> {
Class<T> type;
public ClassTypeCapture(Class<T> type){
this.type = type;
}
public boolean f(Object obj){
return type.isInstance(obj);
}
public static void main(String[] args) {
ClassTypeCapture<Teller> ctt =new ClassTypeCapture<Teller>(Teller.class);
System.out.println(ctt.f(new Automobile()));
}
}
有时必须通过引入类型标签来对擦除进行补偿,这意味着你要显示的传递你的类型Class对象,以便你在类型表达式中用到它
- 10.泛型数组
可以使用带范型参数值的类声明数组,却不可有创建数组
import java.lang.reflect.Array;
public class GenericArray2<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArray2(Class<T> type,int size){
array = (T[]) Array.newInstance(type, size);
}
public void put(int index,T item){
array[index] = item;
}
public T get(int index){
return array[index];
}
public T[] pop(){
return array;
}
public static void main(String[] args) {
}
}
类型标记Class<T>被传递到构造器中,以便从擦除中恢复,使得我们可以创建需要的实际类型的数组
- 11.泛型问题
1.任何基本类型都不能作为类型参数
2.实现参数化接口
一个类不能实现同一个泛型接口的两种变体,由于擦除的原因这两个变体会成为相同的接口
3.转型和警告
使用带有泛型类型参数的转型或instanceof不会有任何效果
4.重载
由于擦除的原因,重载将产生相同的类型签名
于此不同的是当被擦除的参数不能产生惟一的参数列表时,必须提供明显有区别的方法名