java编程之泛型(二)

第三部分 泛型接口以及泛型方法


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();
    }
}

以上就是边界的概念。其实所谓边界,简单说就是为泛型的参数类型做一个限定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值