第三部分 泛型接口以及泛型方法
1、接口泛型
接口使用泛型与类使用泛型没有太大的区别
interface InterGenerics<T>{ T next();}
//实现接口InterGenerics,生成Fibonacci数列
class Fibonacci implements InterGenerics<Integer>{
private int count = 0;
//将int型自动装箱成它所对应的包装类Integer
public Integer next(){return fib(count++);}
private int fib(int n){
if(n<2) return 1;
return fib(n-2) + fib(n-1);
}
}
通过上例我们可以看到java泛型的一个局限性:基本类型是无法作为类型参数的。不过java SE5及之后版本已经解决了这个问题,具有自动装箱和拆箱(也称打包和拆包)的功能。将基本类型int,double等装箱成Integer,Double等包装类,是自动装箱过程,反之则是自动拆箱过程
2、泛型方法
在定义泛型方法时,只需将泛型参数列表置于返回类型之前
public class GenericMethod {
public <T> void f(T t){
System.out.println(t.getClass());
}
public static void main(String[] args){
GenericsMethod gm = new GenericMethod();
gm.f("test");
gm.f(2.8);
gm.f(2.8f);
gm.f('q');
}
}
泛型方法能够让该方法独立于类而发生改变。在我们的代码设计当中应当尽量使用泛型方法。
注意:对于一个static方法,它是无法访问泛型类的类型参数,如果static需要使用泛型,就必须将它自身方法变为泛型方法
这里还有一点:我们在使用泛型类创建其对象时,必须指定类型参数的值,就像下面:
class Example<T>{
public static void main(){
Example<String> e = new Example<String>();
}
}
而我们在使用泛型方法时不必指明参数,编译器会自动找出具体类型,这个过程叫做:类型参数推断(来自Java In Thinking)
可变参数与泛型方法
import java.util.*;
public class GenericVarargs {
public static <T> List<T> getList(T...args) {
List<T> container = new LinkedList<T>();
for(T item: args)
container.add(item);
return container;
}
public static void main(String[] args){
List<String> ls1 = getList("exmple");
System.out.println(ls1);
List<Character> ls2 = getList('a');
System.out.println(ls2);
}
}
第四部分 边界
浅谈擦除问题:
import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args){
List<String> c1 = new ArrayList<String>();
List<Integer> c2 = new ArrayList<Integer>();
System.out.println(c1.getClass()==c2.getClass());
}
}/*output:
true
*/
上面代码的输出结果告诉我们在这个程序中`ArrayList<String>与ArrayList<Integer>`是相同的类型,这是为什么呢?
这是由于java泛型是由擦除来实现的,也就是当我们使用泛型时,任何具体的类信息都会被擦除。因此`List<String>和List<Integer>`在运行时实际是相同的类。这两种类被擦除成它们的“原生”类型List.
再来看这样一个例子
class HasF{
public void f(){System.out.println("HasF.f()");}
}
class Operator<T> {
private T obj;
public Operator(T obj){this.obj = obj;}
//operate方法中的obj试图调用HasF类当中的f()
//出现编译器报错:The method f() is undefined for the type T
public void operate(){obj.f();}
}
public class Operation{
public static void main(String[] String){
HasF hf = new HasF();
Operator<HasF> operator = new Operator<HasF>(hf);
operator.operate();
}
}
造成以上错误原因是:由于有了擦除,虽然Operator在创建对象时将HasF对象最为参数传入,但是在Operator由于参数类型T是不确定的,所以它内部的operate方法却无法通过java编译器将自己必须在obj上调用f()这一需求映射到HasF拥有f()这一事实上。
于是我们引进边界来对参数类型T做一个限定。限制T必须是HasF的派生类,这里将重用extends关键字,修改后的代码如下:
class HasF{
public void f(){System.out.println("HasF.f()");}
}
//制定T的边界HasF
class Operator<T extends HasF> {
private T obj;
public Operator(T obj){this.obj = obj;}
//此时调用f()编译器不会报错,代码正常运行
public void operate(){obj.f();}
}
public class Operation{
public static void main(String[] String){
HasF hf = new HasF();
Operator<HasF> operator = new Operator<HasF>(hf);
operator.operate();
}
}
以上就是边界的概念。其实所谓边界,简单说就是为泛型的参数类型做一个限定