Java编程思想:擦除的补偿(数组泛型处,我有很多细节没有研究)

import sun.net.www.content.text.Generic;

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

public class Test {
    public static void main(String[] args) {
//        ClassTypeCapture.test();
//        InstantiateGenericType.test();
        CreatorGeneric.test();
    }
}

/*
    15.8 擦除的补偿
        ——标记下,这个地方乱七八糟,我有点混乱

    因为擦除的问题,我们有时候必须通过引入类型标签来对擦除进行补偿。这意味
    着你需要显式地传递你的类型的Class对象,以便你可以在类型表达式中使用它。
    在前面的例子中对instanceof的尝试最终失败了,因为其类型信息已经被擦除了。
    如果引入类型便签,就可以转而使用动态的isInstance()。(前面的失败完全是
    因为非要将一个具体类型标签符给一个没有指明的类型标签)
 */
class Building { }
class House extends Building { }
class ClassTypeCapture<T>{
    Class<T> kind;
    public ClassTypeCapture(Class<T> kind){
        this.kind=kind;
    }

    public boolean f(Object arg) {
        /*
            这个地方应该涉及到了Class对象储存信息方面的问题
         */
        return kind.isInstance(arg);
    }

    public static void test() {
//        ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<>(Building.class);
//        System.out.println("ctt1.f(new Building()):"+ctt1.f(new Building()));
//        System.out.println("ctt1.f(new House()):"+ctt1.f(new House()));
//
//        System.out.println();

        ClassTypeCapture<House> ctt2 = new ClassTypeCapture<>(House.class);
        System.out.println("ctt2.f(new Building()):"+ctt2.f(new Building()));
        System.out.println("ctt2.f(new House()):"+ctt2.f(new House()));
    }
}

/*
    我把上一个案例的代码,和解读贴在这儿,帮助我理解一下这个问题:

        即使kind被存储为Class<T>,擦除也就意味着它实际将被存储为Class,没有任何参数。
        因此当你在使用它时,例如在创建数组时,Array.newInstance()实际上并未拥有kind
        所蕴含的类型信息,因此这不会产生具体的结果,所以必须转型,这将产生一条令你无法满
        意的警告。
 */
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(9);
//        System.out.println(Arrays.toString(stringArray));
    }
}

/*
    15.8.1 创建类型实例

    需求:
        解决在在泛型类中不能用T t创建对象的方案。Java中可以传递一个工厂对象,并使用它来
        创建新的实例。最便利的工厂对象就是Class对象,因此如果使用类型标签,那么你可以使用
        newInstance()来创建这个类型的新对象。
 */
class ClassAsFactory<T>{
    T x;
    T y;
    Class<T> k;

    public ClassAsFactory(Class<T> kind) {
        try{
            x = kind.newInstance(); //这个地方没有将kind对象存给任何被擦除了的对象哦

            System.out.println("x:"+x.getClass().getName());

            /*
                很奇怪,我这儿并有发生前文描述的任何问题?
             */
            k=kind;
            y = k.newInstance();
            System.out.println("y:"+y.getClass().getName());



        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

}
class Employee { }

//这个方案由于问题不是在编译期报出来,故不推荐
class InstantiateGenericType {
    static void test() {
        new ClassAsFactory<Employee>(Employee.class);
        System.out.println("ClassAsFactory<Employee> succeeded");
//
//        ClassAsFactory<Integer> f1 =
//               new ClassAsFactory<>(Integer.class);
    }
}

//Sun建议使用的显式工厂
interface Factory<T>{
    T create();
}
class Foo2<T>{
    private T x;
    public <F extends Factory<T>> Foo2(F factory){
        x = factory.create();
    }
}
class IntegerFactory implements Factory<Integer>{

    @Override
    public Integer create() {
        return new Integer(0);
    }
}
/*
    这个为什么会报错啊!!!

class Widget{
    //嵌套类实现一个工厂,但是报错了,说是循环实现
    static class Factory implements Factory<Widget>{
        public Widget create() {
            return new Widget();
        }
    }
}
*/
class FactoryConstraint{
    static void test() {
        new Foo2<Integer>(new IntegerFactory());
//        new Foo2<Widget>(new Widget.Factory());
    }
}


/*
    最后一种在泛型类中实例参数对象的方法:使用模板方法设计模式。
 */
abstract class GenericWithCreate<T>{
    final T element;
    GenericWithCreate(){
        element=create();
    }
    abstract T create();
}
class X{}
class Creator extends GenericWithCreate<X>{

    @Override
    X create() {
        return new X();
    }
    void f(){
        System.out.println(element.getClass().getSimpleName());
    }
}
class CreatorGeneric{
    static void test() {
        Creator c = new Creator();
        c.f();
    }
}

/*
    最后做一个自己的实验,验证一下现在真的不可以在泛型类中创建参数化对象么?
 */
class A<T>{
    T t;
    A(){
        //t = new T(); 果然不可以
    }
}

/*
    15.8.2 泛型数组
        ——这部分东西,我暂时不研究了,很枯燥,而且感觉价值并不是太高
    
    解决不能创建泛型数组的方案:在任何想要创建泛型数组的地方都使用ArrayList
 */
class ListOfGenerics<T>{
    private List<T> array = new ArrayList<>();
    public void add(T item){array.add(item);}
    public T get(int index){return array.get(index);}
}


/*
    有时,你仍旧希望创建泛型类型的数组(例如,ArrayList内部使用的是数组)。有趣的是
    你可以按照编译器喜欢的方式来定义一个引用,例如:
 */
class H<T> {}
class ArrayOfH{
    static H<Integer>[] gia;
}

/*
    编译器接受这个程序,而不会产生任何警告。但是,永远都不能创建这个确切类型的数组(包括
    参数类型),因此这有点令人困惑。既然所有数组无论它们持有的类型如何,都具有相同的结构
    (每个数组槽位的尺寸和数组的布局),那么看起来你应该能够创建一个Object数组,并将其
    装换成所希望的数组类型。事实上这可以编译,但是不能运行,它将产生ClassCaseException
 */
class ArrayOfGeneric{
    static final int SIZE = 100;
    static H<Integer>[] gia;

    @SuppressWarnings("unchecked")
    static void test() {
        gia = (H<Integer>[])new H[SIZE];
    }
}

/*
    成功创建泛型数组的唯一方式就是,创建一个被擦除类型的新数组,然后对其转型
 */

 

上一篇:JAVA多态


下一篇:914. X of a Kind in a Deck of Cards