泛型中的无界通配符

《JAVA编程思想》这本书确实是非常的晦涩难懂,所以我决定博客也跟着阅读进度一起更新,今天的阅读是无界通配符,废话不多说,来看下面的代码:

import java.util.ArrayList;
import java.util.List;
public class UnboundedWildcards1
{
    static List list1;
    /* 警告:List使用的是原始类型,但是List1是一个泛型容器
     * 所以此时应该使用泛型*/
    static List<?> list2;
    static List<? extends Object> list3;
    static void assign1(List list)/*报错信息和第5行一样*/
    {
        list1 = list;
        list2 = list;
        //list3 = list;
        /* 警告:
         * 泛型转换错误,这个时候只要List由原始类型转换为
         * 任何泛型都可以。*/
    }
    static void assign2(List<?> list)
    {
        list1 = list;
        list2 = list;
        list3 = list;
        /* 我觉得这个地方list3 = list并没有报错的原因还是因为
         * 类型擦除的原因*/
    }
    static void assign3(List<? extends Object> list)
    {
        list1 = list;
        list2 = list;
        list3 = list;
        /*不报错原因和assign2方法中解释的原因一样*/
    }
    public static void main(String[] args)
    {
        assign1(new ArrayList());
        /* 警告:
         * 1.ArrayList并不能转化成类型或者容器
         * 2.ArrayList需要配合泛型进行使用*/
        assign2(new ArrayList());
        /*警告:警告内容同上*/
        //assign3(new ArrayList());
        /* 警告:
         * 泛型转换异常,ArrayList虽然可以转化为List和List<?>
         * 但是由于assign3方法中参数要求转化为
         * List<? extends Object>所以这个时候转型错误*/
        assign1(new ArrayList<String>());
        assign2(new ArrayList<String>());
        assign3(new ArrayList<String>());
        //以上所有泛型都可以转化为<?>从而被所有接受
        List<?> wildList = new ArrayList();
        wildList = new ArrayList<String>();
        assign1(wildList);
        assign2(wildList);
        assign3(wildList);
    }
}

从这段代码可以看出,

List<?> a1 = new ArrayList<>();
List<? extends Object> a2 = new ArrayList<>();
a2 = a1;//ok
List<? extends Object> a3 = new ArrayList<>();
a2 = a3;//ok

但是可以从第一段代码可以看出,两者对于原始类型的转化严格程度是不同的,看下面这段代码:


import java.util.*;
//import static net.mindview.util.Print.*;
class BerylliumSphere{}
public class ArrayOfGenericType
{
    public static void main(String[] args){
        List a0 = new ArrayList();
        List<Object> a1 = new ArrayList<>();
        List<?> a2 = new ArrayList<>();
        List<? extends Object> a3 = new ArrayList<>();
        List<? super Object> a4 = new ArrayList<>();

        a0 = a1;
        a0 = a2;
        a0 = a3;
        a0 = a4;
        //可见原型容器可以接受所有参数类容器的转型

        a1 = a0;
        a1 = a2;//List<?>不能转换位List<Object>
        a1 = a3;//List<? extends Object>不能转换位List<Object>
        a1 = a4;

        a2 = a0;//这个地方并没有出现警告
        a2 = a1;
        a2 = a3;
        a2 = a4;

        a3 = a0;//警告:在未检查的情况下进行泛型转换
        a3 = a1;
        a3 = a2;
        a3 = a4;

        a4 = a0;//警告:在未检查的情况下进行泛型转换
        a4 = a1;
        a4 = a2;//List<?>不能转型为List<? super Object>,范围扩大
        a4 = a3;//List<? extends Object>不能转型为List<? super Object>,范围扩大

        a0.add(new Object());
        a1.add(new Object());
        a2.add(new Object());//编译错误,无解通配符实际范围要小于Object
        a3.add(new Object());//编译错误,? extends Object不能将Object作为参数是因为泛型的范围小于Object
        a4.add(new Object());
    }
}

可以看出 ? extends Object 相比较于 ? 对于原始类型的要求更加严格。
接下来再看一段代码

import java.util.*;
@SuppressWarnings("rawtypes")
public class unboundedWildcards2
{
    static Map map1;
    static Map<?,?> map2;
    static Map<String, ?> map3;
    static void assign1(Map map){map1 = map;}
    static void assign2(Map<?,?> map){map2 = map;}
    static void assign3(Map<String, ?> map){map3 = map;}
    public static void main(String[] args)
    {
        assign1(new HashMap());
        assign2(new HashMap());
        //assign3(new HashMap());
        /* 警告:
         * 因为这个地方第一个泛型指明了参数String,所以
         * 这个时候用原始类型进行转换就会报错*/
        assign1(new HashMap<String,Integer>());
        assign2(new HashMap<String,Integer>());
        assign3(new HashMap<String,Integer>());
    }
}

从上面的代码可以看出,如果指定了泛型那么泛型就必须符合指定的泛型。
下面用一大段代码总结一下无界通配符这一个小节:

import java.util.*;

class Holder<T>
{
    private T value;
    public Holder() {}
    public Holder(T val){ value = val;}
    public void set(T arg){ value = arg;}
    public T get(){return  value;}
    public boolean equals(Object obj)
    {
        return value.equals(obj);
        //Object的.equals方法是判断对象是否相同,而不是值
    }
}
@SuppressWarnings({"unused","rawtypes"})
public class Wildcards
{
    static void rawArgs(Holder holder, Object arg)
    {
        //holder.set(arg);
        /* 警告:
         * 警告的原因是holder是一个泛型容器,但是这个地方用的
         * 是原始类型,导致类型不正确*/
        //holder.set(new Wildcards());
        // 警告:原因同上
        //T t = holder.get();
        // 错误:在个方法中并不存在泛型
        Object obj = holder.get();
        //***虽然Holder.get()返回的是一个泛型,但是仍然可以转换
        //***为Object类型
    }
    static void unboundedArg(Holder<?> holder, Object arg)
    {
        //holder.set(arg);
        /* 警告:由于Holder是一个泛型容器,这个时候使用了无
         * 界通配符将导致Object不能作为.set()方法的参数*/
        //holder.set(new Wildcards());
        // 警告:原因同上
        //T t = holder.get();
        // 错误:不存在T作为类型
        Object obj = holder.get();
    }
    static <T> T exact1(Holder<T> holder)
    {
        T t = holder.get();
        return t;
    }
    //*** .get()泛型方法+泛型容器将不会报错
    // 如果将参数中的Holder<T>改为Holder将导致报错,并且
    // 错误原因为.get()方法的返回值为Object,不能将Object转化
    // 为T类型
    static <T> T exact2(Holder<T> holder,T arg)
    {
        holder.set(arg);
        T t = holder.get();
        return t;
    }
    /* 想要泛型容器Holder的.set()方法不报错,要注意:
     * .set()方法的参数要求为泛型,并且其参数的范围应该要
     * 小于Holder容器的泛型参数的范围,否则将报错,比如:
     * 如果arg的泛型为<Object>但holder的为<? extends Object>
     * 就会导致报错,但是如果arg的泛型为<Object>但是holder的为
     * <? super Object>就不会报错,但是这样的话.get()方法
     * 就会报错*/
    static <T> T wildSubtype(Holder<? extends T>holder, T arg)
    {
        //holder.set(arg);
        //* 错误:原因同上,holder的泛型范围不能小于arg的范围
        T t = holder.get();
        return t;
    }
    static <T> void wildSupertype(Holder<? super T> holder,T arg)
    {
        holder.set(arg);
        //T t = holder.get();
        //* 错误:原因同上,holder的范围大于t,不能向下转型
        Object t = holder.get();
        //虽然这个不会报错,但是已经失去了类型信息
    }
    public static void main(String[] args)
    {
        Holder raw = new Holder<Long>();
        //或者
        raw = new Holder();

        Holder<Long> qualified = new Holder<Long>();
        Holder<?> unbounded = new Holder<Long>();
        Holder<? extends Long> bounded = new Holder<Long>();
        Holder<? super Long> spbounded = new Holder<Long>();
        Long lng = 1L;

        rawArgs(raw,lng);
        rawArgs(qualified, lng);
        rawArgs(unbounded, lng);
        rawArgs(bounded, lng);
        rawArgs(spbounded, lng);
        /* 由上面的结果可以看出不管holder是怎么样的泛型参数
         * 都可以由原型Holder来进行引用*/

        unboundedArg(raw, lng);
        unboundedArg(qualified, lng);
        unboundedArg(unbounded, lng);
        unboundedArg(bounded, lng);
        rawArgs(spbounded, lng);
        /* 看来不仅是原型Holder可以引用泛型holder,holder<?>
         * 也可以引用带各种泛型的holder*/

        //Object r1 = exact1(raw);
        /* 警告:1.exact1方法需要的是泛型的holder容器,不能
         * 使用原型容器 2.raw所引用的原型容器在没有检查的情况
         * 下转换成了泛型容器*/
        Long r2 = exact1(qualified);
        Object r3 = exact1(unbounded);
        Long r4 = exact1(bounded);
        //Long r5 = exact1(spbounded);
        /* 错误:此时返回值是Long的超集,故不能用其子集表示它*/

        Long r6 = exact2(raw, lng);
        /* 警告:原因是:1.参数不正确,exact2()方法需要一个泛型
         * Holder容器而不是一个原型 2.raw所引用的原型容器在
         * 没有进行检查的情况下进行了转换*/
        Long r7 = exact2(qualified, lng);
        //Long r8 = exact2(unbounded, lng);
        /* 错误:可以理解为这个时候unbounded使用的无界通配符
         * 的范围大于exact2()中的泛型T所以此时报错*/
        //Long r9 = exact2(bounded, lng);
        /* 错误:理由同上*/
        Object r10 = exact2(spbounded, lng);

        //Long r11 = wildSubtype(raw, lng);
        /* 警告:原因是:1.参数不正确,exact2()方法需要一个泛型
         * Holder容器而不是一个原型 2.raw所引用的原型容器在
         * 没有进行检查的情况下进行了转换*/
        Long r12 = wildSubtype(qualified, lng);
        Object r13 = wildSubtype(unbounded, lng);
        Long r14 = wildSubtype(bounded, lng);
        Object r15 = wildSubtype(spbounded, lng);
        //由上面的情况可以看出<? extends T>这种泛型可以减少泛
        //型所带来的烦恼

        //wildSupertype(raw, lng);
        /* 警告:原因是:1.参数不正确,exact2()方法需要一个泛型
         * Holder容器而不是一个原型 2.raw所引用的原型容器在
         * 没有进行检查的情况下进行了转换*/
        wildSupertype(qualified, lng);
        //wildSupertype(unbounded, lng);
        /* 这是我觉得非常特殊的一个情况,可能是泛型设计是的一个
         * 错误,看下面的代码:
         * List<? super Object> l1 = new ArrayList<Object>();
         * List<?> l2 = new ArrayList<Object>();
         * l1 = l2;//错误
         * 看来在不管什么情况下就算是使用了super都不能进行引用
         * 但是<?>和<? extends Object>之间却可以很好的转化*/
        //wildSupertype(bounded, lng);
        wildSupertype(spbounded, lng);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值