泛型使用建议

一.不要在新代码中使用原生态类型

     虽然不提供类型参数,使用集合类型和其他泛型也仍然是合法的,但是这样就失掉了泛型在安全性(编译时检查)和表述性方面的所有优势。

A:原生态类型如 List:不带任何类型参数的泛型名称

B:参数化类型如List<String> :表示元素类型为String的列表

C:无限制的通配符类型如List<?>:表示元素为未知类型

 

二.消除非受检警告

  用泛型编程的时候,会遇到许多的编译器警告:非受检强制转化警告(unchecked cast warning)、非受检方法调用警告、非受检普通数组创建警告以及非受检转换警告(unchecked conversion warnings)。要尽可能的消除每一个非受检警告。如果消除了所有警告就可以确保代码是类型安全的。

        如果无法消除警告,同时又可以证明引起警告的代码是类型安全的。可以用一个@SuppressWarnings("unchecked")注解来禁止这条警告。

三.优先考虑泛型方法

如:

public static Set union(Set s1, Set s2) {

      Set result = new HashSet(s1);

       result.addAll(s2);

       return result;

}

修改为:

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {

    Set<E> result = new HashSet<E>(s1);

    result.addAll(s2);

     return result;

}

当前这个版本的union方法即为一般的泛型方法,但是它有一个限制,要求三个集合的类型(两个输入参数及一个返回值)必须全部相同。利用有限制的通配符类型可以使这个方法变得更回灵活。

四.利用有限制通配符来提升API的灵活性

1、Extends

有时候,需要的灵活性要比不可变类型所能提供的更多。考虑第26条中的堆栈下面就是他的公共API:

public class Stack<E> {

    public Stack();

    public void push(E e);

    public E pop();

    public boolean isEmpty();

}

 

假设增加一个方法,按顺序将一系列的元素放到堆栈中:



public void pushAll(Iterable<E> src) {

    for (E e: src)

        push(e);

}

 

如果尝试这样做:

Stack<Number> s = new Stack<Number>();

Iterable<Integer> i = ...;

s.pushAll(integers);
  1.  

从逻辑上讲,这样应该是允许的,因为Integer是Number的子类,应当允许将Integer放到类型为Number的堆栈中。但实际运行的时候会提示Iterable<Number>与Iterable<Integer>不兼容。原因在于Iterable<Integer>并不是Iterable<Number>的子类型(参数化类型是不可变的)。

Java提供了一种特殊的参数化类型,称为有限制的通配符类型来处理类似的情况。使用有限制的通配符Iterable<? extends E>即可解决这个问题(注意,确定了子类型后,第一个类型便都是自身的子类型),修改后的程序如下:


public void pushAll(Iterable<? extends E> src) {

    for (E e: src)

        push(e);

}

修改之后,不仅Stack可以正确无误地编译,没有通过初始的pushAll声明进行编译的客户端代码也一样可以。因为Stack及其客户端正无误的进行了编译,你就知道一切都是类型安全的了。

2、Super

对应的,假如我们要编译一个popAll方法,初次尝试如下:


public void popAll(Collection<E> dst) {

    while(!isEmpty())

        dst.add(pop());

}

如果目标集合的元素类型与堆栈完全匹配,这段代码编译时还是会正确无误的。但是,假设你有一个Stack<Number>和类型Object变量,如果从堆栈中弹出一个元素,并将它保存在该变量中,它的编译和运行都不会出错,考虑如下代码 :

Stack<Number> numberStack = new Stack<Number>();

Collection<Object> objects = ...;

numberStack.popAll(objects);

试着用上述的popAll版本编译这段代码,就会得到一个类似于第一次pushAll时的错误:Collection<Object>不是Collection<Number>的子类型。

 

对于这种情况,java同样提供了一种对应的有限制通配符来解决,popAll的输入参数类型不应该为“E的集合”,而应该为“E的某种超类的集合”。通配符:Collection<? super E>,根据这种方法修改后的代码如下:

public void popAll(Collection<? super E> dst) {
    
    while(!isEmpty())

        dst.add(pop());

}

 

3、总结

由上面这两种情况可以看出,有限制的通配符类型放宽了检查的类型,为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型。如果某个输入参数既是生产者,又是消费者,那么通配符类型就没有什么好处了,因为需要的是严格的类型匹配,这是不用任何通配符而得到的。

下面的助记符便于让你记住要使用哪种通配符类型类型:
PESC表示producter-extends, consumer-super。

如果参数化类型表示一个T生产者,就使用<? extends T>;如果它表示一个T消费者,就使用<? super T>。
在我们的Stack实例中,pushAll的src参数产生E实例供Stack使用,因此src相应的类型为Iterable<? extends E>;popAll的dst参数通过Stack消费E实例,因此dst的相应类型为Collection<? super E>。PECS这个助记符突出了使用通配符类型的基本原则。

五、泛型列表优于泛型数组

六、泛型的标记符含义

E - Element (在集合中使用,因为集合中存放的是元素)

 - Type(Java 类)

 - Key(键)

 - Value(值)

 - Number(数值类型)

? -  表示不确定的java类型

七、泛型使用

1.泛型类

定义一个泛型类:public class GenericClass<T>{}

2.泛型接口

定义一个泛型接口:public interface GenericIntercace<T>{}

实现泛型接口:public class ImplGenericInterface1<T> implements GenericIntercace<T>

3.泛型方法

定义一个泛型方法: private static<T> TgenericAdd(T a, T b) {}

4.泛型中的约束和局限性

1,不能实例化泛型类
2,静态变量或方法不能引用泛型类型变量,但是静态泛型方法是可以的
3,基本类型无法作为泛型类型
4,无法使用instanceof关键字或==判断泛型类的类型
5,泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的
6,泛型数组可以声明但无法实例化
7,泛型类不能继承Exception或者Throwable
8,不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值