java通配符

找了找关于java通配符的一些资料,下面两则写的比较清晰
1.java通配符

下面是正文:
      固定的泛型类型系统使用起来并没有那么令人愉快。Java的设计者发明了一种巧妙(仍然是安全的)“解决方案”:通配符类型。
例如:Pair<? extends B>,表示任何泛型Pair类型,它的类型参数是B的子类,如Pair<BSub>,但不是Pair<Date>。
构造一个方法:
    public static void executeFun(Pair<BSub> p){
        p.getFirst().fun();
        p.getSecond().fun();
    }
不能将Pair<B>传给这个方法,方法的功能受到了很大的限制。解决方法是:使用通配符类型。
    public static void executeFun(Pair<? extends B> p){
        ...
    }
类型Pair<BSub>是Pair<? extends B>的子类型。
使用通配符会通过Pair<? extends B>的引用破坏Pair<BSub>吗?答案是不能。
Pair<BSub> bsp = new Pair<BSub>();
Pair<? extends B> bp = bsp;//ok
bp.setFirst(new B());//Error
对setFirst的调用有一个类型错误。找知道其中缘由,请仔细看看类型Pair<? extends B>。它的方法如下所示:
? extends B getFirst();
void setFirst(? extends B);
这样不可能调用setFirst方法。编译器只知道它需要某个B类型的字类型,但不知道具体是什么类型。它拒绝传递任何特定的类型---毕竟,?不能用

来匹配。
使用getFirst就不存在这个问题:将getFirst的返回值赋给一个B的引用是完全合法的。
这就是引入有限定的通配符的关键之处。现在已经有办法区分安全的访问器方法和不安全的更改器方法了。
 
通配符的超类型限定
通配符限定与类型变量限定十分相似。但是,它还有一个附加的能力,即可以指定一个超类型限定,如下所示:
? super BSub
这个通配符限制为BSub的所有超类型。已有的super关键字十分准确的描述了这种关系。
为什么要这样做?带有超类型限定的通配符的行为与前面介绍的相反。可以向方法提供参数,但不能使用返回值。例如,Pair<? super BSub>有方法
void setFirst(? super BSub)
? super BSub getFirst()
编译器不知道setFirst方法的确切类型,但是可以用任意BSub对象(或子类型)调用它,而不能用B对象调用。然而,如果调用getFirst,返回的对

象类型不会得到保证,只能把它赋给一个Object,如果要将返回值赋给一个非Object的变量要使用强制的类型转换。直观地讲,带有超类型限定的通

配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
 
下面是超类型限定的另一种应用。Comparable接口本身就是一个泛型类型。它被声明如下:
public interface Comparable<T> {
    public int compareTo(T o);
}
在此,类型变量指示了o参数的类型。例如,String类实现Comparable<String>,它的compareTo方法被声明为
public int compareTo(String o)
很好,显式的参数有一个正确的类型。在JDK1.5之前,o是一个Object,并且该方法的实现需要强制的类型转换。因为Comparable是一个泛型类型,

也许可以把ArrayAlg类的min方法做得更好一些?可以这样声明:
    public static <T extends Comparable<T>> T min(T[] a){
        if(a == null || a.length == 0){
            return null;
        }
        T t = a[0];
        for(int i=1;i<a.length;i++){
            if(t.compareTo(a[i]) > 0){
                t = a[i];
            }
        }
        return t;
    }
看起来,这样写只适用T extends Comparable更彻底,并且对于许多类来讲会工作得更好。例如,如果计算一个String数组的最小值,T就是String

类型的,而String是Comparable<String>的字类型。但是,当处理一个GregorianCalendar对象的数组时,就会出现问题。GregorianCalendar是

Calendar的子类,并且Calendar实现了Comparable<Calendar>。因此GregorianCalendar实现的是Comparable<Calendar>,而不是

Comparable<GregorianCalendar>。
在这种情况下,超类型可以用来进行救助:
    public static <T extends Comparable<? super T>> T min(T[] a){ ... }
现在compareTo(? super T)
有可能它被声明为使用类型T的对象,也有可能使用T的超类型(例如,当T是GregorianCalendar)。无论如何,传递一个T类型的对象给compareTo方

法都是安全的。
对于初学者来说,<T extends Comparable<? super T>>这样的声明看起来有点吓人。但很遗憾,因为这一声明的意图在于帮助应用程序员排除调用

参数上的不必要的限制。对泛型没有兴趣的应用程序员可能很快就学会掩盖这些声明,想当然地认为库程序员做的都是正确的。如果是一名库程序员

,一定要习惯于通配符,否则,就会受到用户的责备,还要在代码中随意地添加强制类型转换直至代码可以编译。
 
无限定通配符
还可以使用无限定的通配符,例如,Pair<?>。咋看起来好像和原始的Pair类型一样。实际上,有很大的不同。类型Pair<?>有方法如:
? getFirst()
void setFirst(?)
getFirst的返回值只能赋给一个Object。setFirst方法不能被调用,甚至不能用Object调用。Pair<?>和Pair的本质不同在于:可以用任意的Object

对象调用原始的Pair类的setFirst()方法。
为什么要使用这样脆弱的类型?它对于许多简单的操作非常有用。例如,下面这个方法将用来测试一个pair是否包含了指定的对象,它不需要实际的

类型。
    public static boolean hasNulls(Pair<?> p){
        return p.getFirst() == null || p.getSecond() == null;
    }
如果把它转化成泛型方法,可以避免使用通配符类型:
    public static<T> boolean hasNulls(Pair<T> p)
但是,带有通配符类型的版本可读性更强。
2.子类型和通配符、
1 子类型和替换原理
在java中,如果它们的关系是通过extends或implements建立的,那么这种关系就是父类型与子类型的关系。

替换原理:能够接受父类的就一定可以接受子类型。

注意:List<Number>和List<Integer>不存在子类型关系。但是数组的行为就有所不同:

Integer[] 是Number[]的子类型。

2 拥有extends的通配符
<? extends E>表示E类型的任意子类型(包括本身),而这?就叫做通配符,也就是任意类型。

public static void count(Collection<? extends Integer> ints, int a) {}

3 拥有super的通配符
<? super T>表示T类型的任意父类型(包括本身)。

 public static void count(Collection<? super Integer> ints, int a) {}

4 Get和Put原理
我们怎样决定使用哪种类型的通配符,用extends还是super,或两者都不。

Get和Put原理:当你仅仅从一个结构中获取值时,你应该使用extends。如果只想向一个结构中插入值,你应该使用super。如果想即插入又获取值时

,你因该避免使用通配符。说白了就是放入的条件越宽越好,因为超类能够接受他的任何子类。而取值是越明确越好,所以用子类限定,同时也限制

了插入的范围。

null是属于任何引用类型,而Object是任何类型的超类。

5 数组
在java中,数组子类型是协变的,也就是说当S是T的子类型,那么S[]就是T[]的子类型。相反,List<S>并不是List<T>的子类型。但如果引入通配符

,即List<S>被认为是List<?  super T>的子类型(S是T的超类型)。

6 通配符捕获
当调用泛型方法时,类型参数可以被用于匹配用通配符表示的未知类型,这叫做通配符捕获。

    注意:通配符表示未知类型。

7 通配符的限制
A)实例创建:通配符不能出现在创建对象表达式的顶层,但是嵌套的可以。

List<?> list = new ArrayList<?>(); // 编译错误

Map<String, ? Extends Number> map = new HashMap<String, ? Extends Number>//编译错误

List<List<?>> lists = new ArrayList<List<?>>();//合法。

     B) 泛型方法调用:如果调用一个包含明确类型参数的泛型方法,那么这个类型参数一定不能是通配符。你可以采用类型推断或显示的传递一个

类型参数。

List<?> list = lists.factory();

List<?> list = lists.<Object>factory(); //Object就是明确的类型参数。

List<?> list = lists.<?>factory();

但是嵌套的可以:

List<List<?>> Lists.<List<?>>factory();//合法

 C) 超类:因为创建类实例时,首先要初始化他的父类(创建实例的约束也适合超类),所以在类的声明中,如果超类或接口有类型参数,那么这些

类型参数一定不能是通配符。

class AnyList extends ArrayList<?>{...} //编译错误

class AnotherList implements List<?>{...}//编译错误

但是嵌套的可以:

class NestedList implements ArrayList<List<?>>{...}//合法

转载于:https://www.cnblogs.com/FromNowOn/p/3170376.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值