import java.util.*;

public class Test {
    public static void main(String[] args) {

    }
}

/*
    15.9 边界

    要点:
        1.T继承的顺序,类放必须放在第一位,后面跟接口
        2.类与接口用&进行链接
        3.可以继承于一个类,可以实现多个接口
        4.可以使用边界类的方法
        5.可以使用边界类的公共变量

    在继承的每个层次上添加边界限制
 */

/*
    15.10 通配符

    问题:
        编译器允许你将Fruit放置在这个数组中,这对于编译器来说是有意义的,但是它有一个Fruit[]
        引用——它没有理由不允许将Fruit对象或者任何从Fruit对象继承出来的对象(例如Orange)
        放置在这个数组中。但是,在运行时的数组机制知道它处理的时Apple[],因此会在向数组中放置
        异构类型时抛出异常。

 */

class Fruit{}

class Apple extends Fruit {}
class LittleApple extends Apple {}

class Orange extends Fruit {}
/*
    需求:
        以下转换你是有需求的,但是不允许
 */
class Need{
    static void test() {
        //List<Fruit> fruits = new ArrayList<Apple>();
    }
}
/*
    需求:
        通配符可以满足以上你的需求

        List<? extends Fruit>:具有任何从Fruit继承的类型的列表,但是这并不意味着
        这个List将持有任何类型的Fruit。通配符引用的是明确的类型,它意味着“某种first
        引用没有指定的具体类型的”。因此这个被赋值的List必须持有诸如Fruit或Apple这样的
        某种指定类型,但是为了向上转型为fruit,这个类型是什么样的,没人关心。

 */
class Need1{
    static void test(){
//        List<? extends Fruit> fruits = new ArrayList<Apple>();

//        fruits.add(new Apple());
//        fruits.add(new Fruit());
//        fruits.add(new Object());
    }
}

/*
    15.10.1 编译器有多聪明

    尽管add()将接受一个具有泛型参数类型的参数,但是contains()和 indexOf()将接受Object类型的
    参数。因此当你指定一个ArrayList<? extends Fruit>时,add()的参数就变成了“? extends Fruit”。
    从这个描述中,编译器并不能了解这里需要的是Fruit的哪个具体子类型,因此它不会接受任何类型的
    Fruit。如果先经Apple先向上转型为Fruit也无济于事——编译器直接拒绝对参数列表中涉及通配符
    的方法(如add())的调用。

    在使用contains()和indexOf()时,参数类型时Object,因此不涉及任何通配符,而编译器也将允许
    这个调用。这意味这将由泛型类型的涉及者来决定哪些调用是安全的,并有Object类型作为其
    参数类型。
 */
class CompilerIntelligent{
    static void test() {
        List<? extends Fruit> fruits = Arrays.asList(new Apple());
        Apple a = (Apple)fruits.);
        fruits.contains(new Apple());
        fruits.indexOf(new Apple());
    }
}

/*
    需求:
        为了在类型中使用通配符的情况下禁止这类调用,我们需要在参数列表中使用类型参数。
 */
class Holder<T>{
    private T value;
    public Holder() {}

    public Holder(T val) {value = val;}

    public T get(){return value;}
    public void set(T val){
        value = value;
    }

    public boolean equals(Object obj){
        return value.equals(obj);
    }

    static void main(String[] args) {

        //常规是允许操作的
        Holder<Apple> apples = new Holder<>(new Apple());
        Apple d = apples.get();
        apples.set(d);

        Holder<? extends Fruit> fruits = apples;
        Fruit p = fruits.get();
        //fruits.set(p); //这个方法果然被禁用了,哈哈

    }
}

/*
    15.10.2 逆变

    知识点:
        超类型通配符:可以申明通配符是由某个特定类的任何基类来界定的,方法是
        <? super MyClass>,甚至可以使用类型参数:<? super T>。这使得你
        可以安全的传递一个对象到泛型类型中。
 */
/*
    extends:有继承的意思,可以很明显的感觉到,你持有的可能是我持有的子类

    这是一个错误的理解!!!
    //super:有父类的意思,但是这儿明显没有这个用法,更像是在指定我的父类就是你

 */
/*
    卧槽,我发现我理解错了!super和extends的的确确是相反的两个方向。extends是向下
    而super是向上的。那为什么这个地方可以放进去两个值了,因为这个地方说的是我List容
    器里放的就是你Apple的父类,所以你Apple,以及Apple子类都可以放在里面。

    在来思考一下,为什么extends就不能够放了List<? extends Fruit>说的是我List容器
    里放的就是你Frutit的子类,那完了,到底是哪个子类,谁也搞不清
 */

class SuperTypeWildcards {
    static void test(List<? super Apple> apples) {
        apples.add(new Apple());
        apples.add(new LittleApple());
    }
}

/*
    协变与通配符的一个案例
 */
class GenericReading{

    static List<Apple> apples = Arrays.asList(new Apple());
    static List<Fruit> fruits = Arrays.asList(new Fruit());

    //泛型方法实现的,使用确切的类型参数的版本
    static <T> T readExact(List<T> list){
        );
    }

    //泛型类实现的版本,使用确切的类型参数的版本
    static class Reader<T>{
        T readExact(List<T> list){
            );
        }

        <H> H readExact2(List<H> list){
            );
        }
    }

    //泛型类实现的版本,使用了协变通配符
    static class ConvairanReader<T>{
        T readerConvariant(List<? extends T> list) {
            );
        }
    }

    //测试一:
    static void f1() {

        Apple a = readExact(apples);
        Fruit f1 = readExact(fruits);
        /*
            分析:
                readExact(apples)返回的是一个Apple对象,
                只是在赋值的时候进行了一次转型而已
         */
        Fruit f2 = readExact(apples);
    }

    //测试二:
    static void f2() {
        Reader<Fruit> fruitReader = new Reader<>();

        Fruit f = fruitReader.readExact(fruits);
        /*
            分析:
                首先在声明fruitReader时,由于指定的参数类型时Fruit,
                同时这个对象的readExact方法与这个参数类型绑定了,也
                就说此时的readExact方法,只能接受一个List<Fruit>
                类型

                如果在类里添加一个泛型方法,所有的问题都会迎刃而解。
         */
        //Fruit f = fruitReader.readExact(apples);
    }

    static void f3() {
        ConvairanReader<Fruit> fruitReader = new ConvairanReader<>();

        Fruit f = fruitReader.readerConvariant(fruits);
        /*
            分析:
                虽然在创建对象时指定了类型参数为Fruit,但是方法接受的参数中
                又明确说明了可以接受<? extends Fruit>,所以这个地方是允许
                的。哈哈
         */
        Fruit a = fruitReader.readerConvariant(apples);

    }

    static void test(){

    }

}

/*
    15.10.3 无界通配符
        ——标记一下,这后面有一部分,没有细读,需要是再读吧

    知识点:
        使用无界通配符好像等于使用原生类型,事实上,编译器初看起来是支持这种判断的。
        在很多情况下,编译器很少关心你使用的是原生类型,还是<?>。在这种情况跟中,
        <?>可以被认为是一种装饰,但是它仍旧是有价值的,因为,实际上,它是在声明:
        “我想用Java的泛型来编写这段代码,我在这里并不是要用原生类型,但是在当前这
        种情况下,泛型参数可以持有任何类型”
 */

/*
    需求:
       当你在处理多个泛型参数时,有时允许一个参数可以是任何类型,同时为其他参数
       确定某种特定类型的这种能力会显得很重要。

       List:实际上也是List<Object>
       List<?>:表示具有某种特定类型的非原生List,只是我们不知道这种类型是什么
 */
class UnboundedWildCards2{

    static Map map1;
    static Map<?,?> map2;
    static Map<String,?> map3;

    static void test() {
        map1 = new HashMap();
        map2 = new HashMap();
        map3 = new HashMap(); //这个地方会有警告,但是我用Ide看不到
    }
}

/*
    编译器何时才会关注原生类型和涉及无界通配符类型之间的差异呢?
 */
class Wildcards{
    //这个实验很失败,看不到书中的效果
    static void rawArgs(Holder holder,Object arg){
        holder.set(arg);//看不到警告
        holder.set(new Wildcards());

        holder.get();
    }

    //
    static void unbounedArgs(Holder<?> holder,Object arg){
//        holder.set(arg); 报错了,这儿
    }
}

/*
    15.10.4 捕获转换
        ——没有细读,稍等放放再读吧
 */

Java编程思想:通配符(后面有两个小节,研究的不够深入)的更多相关文章

  1. Java编程思想(11~17)

    [注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第十一章 持有对象 11.1 泛型和类型安全的容器>eg: List<St ...

  2. Java编程思想(后)

    Java编程思想(后) 持有对象 如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序. Java中的库基本类型: List, Set, Queue和Map --- 称为集 ...

  3. Java中的泛型 --- Java 编程思想

    前言 ​ 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Ja ...

  4. Java编程思想 4th 第2章 一切都是对象

    Java是基于C++的,但Java是一种更纯粹的面向对象程序设计语言,和C++不同的是,Java只支持面向对象编程,因此Java的编程风格也是纯OOP风格的,即一切都是类,所有事情通过类对象协作来完成 ...

  5. 33.JAVA编程思想——JAVA IO File类

    33.JAVA编程思想--JAVA IO File类 RandomAccessFile用于包括了已知长度记录的文件.以便我们能用 seek()从一条记录移至还有一条:然后读取或改动那些记录. 各记录的 ...

  6. JAVA编程思想——分析阅读

    需要源码.JDK1.6 .编码风格参考阿里java规约 7/12开始 有点意识到自己喜欢理论大而泛的模糊知识的学习,而不喜欢实践和细节的打磨,是因为粗心浮躁导致的么? cron表达式使用 设计能力.领 ...

  7. 《Java编程思想》学习笔记(二)——类加载及执行顺序

    <Java编程思想>学习笔记(二)--类加载及执行顺序 (这是很久之前写的,保存在印象笔记上,今天写在博客上.) 今天看Java编程思想,看到这样一道代码 //: OrderOfIniti ...

  8. #Java编程思想笔记(一)——static

    Java编程思想笔记(一)--static 看<Java编程思想>已经有一段时间了,一直以来都把笔记做在印象笔记上,今天开始写博客来记录. 第一篇笔记来写static关键字. static ...

  9. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  10. Java编程思想重点笔记(Java开发必看)

    Java编程思想重点笔记(Java开发必看)   Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面试过程中,而 ...

随机推荐

  1. 零元学Expression Blend 4 - Chapter 32 简单轻松的学会如何使用Visual States(上)

    原文:零元学Expression Blend 4 - Chapter 32 简单轻松的学会如何使用Visual States(上) Visual State Manager中文翻译为视觉状态管理器,这 ...

  2. 零元学Expression Blend 4 - Chapter 1 缘起

    原文:零元学Expression Blend 4 - Chapter 1 缘起 本来都使用Adobe相关工具从事设计工作的我,因缘际会下,接触到了Expression Blend 4,让我完全的对微软 ...

  3. Qt DLL总结【二】-创建及调用QT的 DLL(三篇)good

    目录 Qt DLL总结[一]-链接库预备知识 Qt DLL总结[二]-创建及调用QT的 DLL Qt DLL总结[三]-VS2008+Qt 使用QPluginLoader访问DLL 开发环境:VS20 ...

  4. 避免用户重复点击按钮(使用Enable:=False,消息繁忙时会有堵塞的问题,只能改用Sleep)

    // 现象描述://    用户点击按钮后程序开始繁忙工作,这时候用户不知道是否成功,就继续点几次//    采用Enalbe = false ... = true的方式发现还会触发点击,分析原因如下 ...

  5. 代理Delegate的小应用(使用setModelData设置下拉日期对话框)

    前言 在平时关于表格一类的的控件使用中,不可避免需要修改每个Item的值,通过在Item中嵌入不同的控件对编辑的内容进行限定,然而在表格的Item中插入的控件始终显示,当表格中item项很多的时候,会 ...

  6. 浅谈网络爬虫爬js动态加载网页(一)

    由于别的项目组在做舆情的预言项目,我手头正好没有什么项目,突然心血来潮想研究一下爬虫.分析的简单原型.网上查查这方面的资料还真是多,眼睛都看花了.搜了搜对于我这种新手来说,想做一个简单的爬虫程序,所以 ...

  7. sublime3使用笔记

    1.ctrl+n 新建一个文件: 2.alt+shift+数字 分屏显示: 3.ctrl+alt+down(向下键) 连选很多行的指定开始位置: 如图: 紧接着再按shift+right(选中需要更改 ...

  8. Spark之从hdfs读取数据

    /user/hive/warehouse/ycapp.db/appindex") ), e(),e().toInt)) (String, String, String) ,,all_post ...

  9. 关于C# 异步

    关于C# 异步操作整理 按照个人的理解, 写一个接口用Task异步操作(态度:接受并且学习,您提出宝贵的经验与理解,我会认真学习): 在主线程中调用异步方法,如果主线程依赖异步方法的返回值那么你一定会 ...

  10. thinkphp5ajax分頁&&搜索後分頁

    //控制器層 //分頁 public function list_january_table(){ //設置當前頁 $page = input("post.page") ? inp ...