import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
//        ErasureAndInheritance.test();
//        ArrayMaker.test();
        FilledListMaker.test();
    }
}

/*
    15.7 擦除的神秘之处

    Class.getTypeParameters()将返回一个TypeVariable对象数组,表示有
    泛型声明所声明的类型参数。我之前确实有过这种需求,但是这个方法不可能给
    我想要的结果

    问题:
        因此,你可以知道诸如类型参数标识符和泛型类型边界这类的信息——你却无法知道用来
        创建某个特定实例的实际的类型参数。Java泛型是使用擦除来实现的,这意味着当你在
        使用泛型的时候,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象
        因此List<String>和List<Integer>在运行时事实上是相同的类型。这两种类型都被
        擦除成了它们的“原生”类型,即List。理解擦除以及应该如何处理它,是你在学习Java
        泛型时面临的最大障碍。

            ——既然被擦除了,为什么我们还能取到我们想要的类型

            ——C++中如果你用了泛型,编译器就会对应的生成这个类,这一点还是蛮
            舒服的。
 */

/*
    15.7.1 C++的方式
    C++:
        template<class T> class Manipulator
        {
            T obj;
            public:
            Manipulator(T x) { obj = x;}
            void manipulator() { obj.f();}
        }

    Java: 无法通过编译
        class Manipulator<T>
        {
            T obj;
            public Manipulator(T x) { obj = x; }
            void manipulator() { obj.f();}
        }

    解决问题:
        为了调用f(),我们必须协助泛型类,给定泛型类的边界,以此告知
        编译器只能接受这个边界的类型。这里重用了extends关键字

    <T extends HasF> : 声明T必须具有类型HasF或者从HasF导出的类型,
    我们说泛型类型参数将擦除到它的第一个边界(可能会有多个边界)。
 */
class HasF {
    public void f() {}
}
class Manipulator<T extends HasF>
{
    T obj;
    public Manipulator(T x) { obj = x; }
    void manipulator() { obj.f();}
}

/*
    在我们的案例中,泛型没有任何贡献。我们完全可以自己执行泛型擦除。这一点很重要:
    只有当你希望使用的类型参数比某个具体类型(以及它的所有子类型)更加“泛化”时——
    也就是说,当你希望代码能够跨多个类工作时,使用泛型才有所帮助。
        ——额,为什么不用接口呢?接口不也是这种功能么,甚至比这个还有强大一点

    因此,类型参数和它们在有用的泛型代码中的应用,通常比简单的类替换要更复杂。但是,
    不能因此而认为<T extends HasF>形式的任何东西都是有缺陷的。例如,如果某个类有
    一个返回T的方法,那么泛型就有所帮助,因此它们之后将返回确切的类型。
        ——我想知道,既然类型已经被擦除了,那返回时是如何返回成T的了,
        难道是编译器私下里进行了一次强制转换?
 */

/*
    15.7.3 擦除的问题

    擦除的主要的正当理由是非泛型的代码到泛型到泛型代码的转变过程。擦除的代价是
    显著的。泛型不能用于显式的引用运行时类型的操作之中,例如转型、instanceof
    操作和new表达式。因为所有关于参数的类型的信息都丢失了。
 */

class GenericBase<T>{
    private T element;
    public void set(T arg) { element = arg;}
    public T get(){return  element;}
}

class Dervied1<T> extends GenericBase<T> {}
class Dervied2 extends GenericBase {}

class ErasureAndInheritance{
    static void test() {
        Dervied2 d = new Dervied2();
        Object obj = d.get();
        d.set(obj);
    }
}

/*
    17.5.4 边界处的动作
 */

/*
    编译器不会给出任何警告,尽管我们从擦除中知道在create()内部的new ArrayList<T>
    中的<T>被移除了——在运行时,这个类的内部没有任何的<T>。但是,即使编译无法知道有
    关create()中的T的任何信息,但是它仍旧可以在编译期间确保你放置到result中的对象
    具有T类型,使其适合ArrayList<T>。
 */
class ArrayMaker<T>{
    //运行时实际保存的是Class<Object>
    private Class<T> kind;

    public ArrayMaker(Class<T> kind) {
        //这个地方在运行时,把一个Class<T>赋给了Class<Object>
        this.kind=kind;
    }

    @SuppressWarnings("unckecked")
    T[] create(int size) {
        return (T[]) Array.newInstance(kind,size);
    }

    public static void test() {
        ArrayMaker<String> s = new ArrayMaker<>(String.class);
        String[] stringArray = s.create();
//        System.out.println(Arrays.toString(stringArray));
    }
}
class ListMaker<T>{
    List<T> create(){return new ArrayList<T>();}

    public static void test() {
        ListMaker<String> s = new ListMaker<>();
        List<String> strs = s.create();
    }
}
class FilledListMaker<T> {
    List<T> create(T t, int n) {
        List<T> result = new ArrayList<T>();
        ; i < n; i++) {
            result.add(t);
        }
        return  result;
    }

    public static void test() {
        FilledListMaker<String> stringMaker = new FilledListMaker<>();
        List<String> list = stringMaker.create();
        System.out.println(list);
    }
}

/*
    因为擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和
    离开方法的地点。这些正是编译器在编译期执行类型检查并插入转型代码的地点。
 */

Java编程思想:擦除的神秘之处的更多相关文章

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

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

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

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

  3. 《Java编程思想》读书笔记(二)

    三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第一章到第十章的内容,这一次记录的是第 ...

  4. 《Java编程思想》读书笔记(四)

    前言:三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第十七章到第十八章的内容,这一次 ...

  5. Java 中的泛型详解-Java编程思想

    Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的 ...

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

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

  7. JAVA编程思想(第四版)学习笔记----4.8 switch(知识点已更新)

    switch语句和if-else语句不同,switch语句可以有多个可能的执行路径.在第四版java编程思想介绍switch语句的语法格式时写到: switch (integral-selector) ...

  8. MyEclipse导入ant项目——Java编程思想

    北门煎饼东门串儿: <JAVA编程思想(Think in Java)>一书中提供了大量源代码,可是项目是用ant构建的.对于用惯了eclipse,netbeans等IDE的同学们可能有些手 ...

  9. Java编程思想第四版勘误

    坊间传说这本书翻译得很烂,我倒觉得还好.虽然看原文更准确,但是如果在具备一定编程思维和基础.能够看出来疑问的情况下,还是看中文更快一些,而且这本书本身也不适合初学者看.当然,错误和不通顺还是有的,而且 ...

随机推荐

  1. 错误:“ResourceDictionary”根元素需要 x:Class 特性来支持 XAML 文件中的事件处理程序。请移除 MouseLeftButtonDown 事件的事件处理程序.

    原文:错误:"ResourceDictionary"根元素需要 x:Class 特性来支持 XAML 文件中的事件处理程序.请移除 MouseLeftButtonDown 事件的事 ...

  2. C#匿名方法返回值赋值给变量

    The problem here is that you've defined an anonymous method which returns a string but are trying to ...

  3. 浏览器禁用cookie后php如何保持session会话-use_trans_sid机制

    原文:浏览器禁用cookie后php如何保持session会话-use_trans_sid机制 为防止浏览器禁用cookie导致服务器会话无法保持,php开发了一个机制,该机制开启后,浏览器发起请求后 ...

  4. Android零基础入门第2节:Android 系统架构和应用组件那些事

    原文:Android零基础入门第2节:Android 系统架构和应用组件那些事 继上一期浅谈了Android的前世今生,这一期一起来大致回顾一下Android 系统架构和应用组件. 一.Android ...

  5. 创建dll动态链接库,并使用java调用

    参考文章:http://www.cnblogs.com/matthew-2013/p/3480296.html http://blog.csdn.net/g710710/article/details ...

  6. Android CTS Test failed to run to conmpletion 测试超时问题

    引用“Android cts all pass 全攻略”里面的一段话: ❀ testcase timeout 测试某个testcase的时候一直出现 “........”,迟迟没有pass或者fail ...

  7. QT5.8 VS2017 编译教程(可以使用VS2017 XP兼容包)

    1.下载QT5.8源码 这个我不做过多解释. 2.安装使用的环境 visual studio 2017  Python Perl  Ruby 安装好,并配置好环境PATH变量. 3.修改错误代码 错误 ...

  8. Delphi For Linux Compiler

    Embarcadero is about to release a new Delphi compiler for the Linux platform. Here are some of the k ...

  9. CentOS7 无法使用yum命令,无法更新解决方法

    前言 设置网卡开机自动启动 设置国内dns服务器系统 修改CentOS-Base.repo中的地址 所参考的文章地址 前言 刚安装完的CentOS7的系统,发现无法使用yum命令进行更新,在更新的时候 ...

  10. 项目集成dubbo

    dubbo 用户指南: http://dubbo.io/User+Guide-zh.htm 开发指南:http://dubbo.io/Developer+Guide-zh.htm#DeveloperG ...