java随笔系列-泛型1

/ java随笔系列 / 没有评论 / 83浏览

简介

Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型。泛型可以用于类、接口、方法,通过使用泛型可以使代码更简单、安全,但是java使用使用了类型擦出,所以是伪泛型,与c++,c#不同。

类型擦出-只有编译阶段知道类型,但是运行阶段JVM无法知道类型。

基本使用

泛型类

泛型带来的最大好处是安全的类型检查,避免代码上转换错误。

public static class InstanceHolder {
    private Object o ;
    public InstanceHolder(Object o) {
        this.o = o;
    }

    public Object getinstance() {
        return o;
    }
}
String value = new String("android");
InstanceHolder instanceHolder = new InstanceHolder(value);
Object o = instanceHolder.getinstance();
String result = (String) o;
System.out.println(result);

对于==instanceHolder==每次取出==value==都需要进行类型转换,虽然可以使用instanceof来避免转换错误但是对于代码编写,安全性检查来说仍然是糟糕的。

public static class InstanceHolder<T> {
    private T o;
    public InstanceHolder(T o) {
        this.o = o;
    }
    public T getInstance() {
        return o;
    }
}
public static void main(String[]args) {
        String value = new String("android");
        InstanceHolder<String> instanceHolder = new InstanceHolder<>(value);
        String result=instanceHolder.getInstance();
}

上面示例中,通过添加泛型,我们不需要类型转换就可以直接获取到真实的类型。类型判断的工作交给了编译器,所以我们可以安全的这样使用

泛型有几点需要注意:

  1. 泛型不可以实例化,因为虚拟机并不知道真实的类型
  2. 不同泛型的泛型类的class对象是相同的(这也说明java泛型是伪泛型)

泛型方法

在普通类上,我们可以直接定义泛型方法,

public static <T>  void patternType(T t,Class<T> c){
    System.out.println(t.getClass().getCanonicalName());
    System.out.println(c.getCanonicalName());
}
public static void main(String[]args){
    patternType("android",String.class);
    patternType("android",Object.class);
}

上面的示例中,patternType方法的两个参数,必须要保证第一个参数的class与第二个参数class的类型相同或者是其class的泛型参数的子类

泛型实例化技巧

上面我们提到过,泛型是不可以实例化的话的,但是我们往往会遇到类似的需求,比如说一个工厂方法需要根据泛型对象,那么有什么技巧的呢?

public static class Dog{}
public interface IFactory<T> {
    T factory();
}
 public static class DogFactory implements IFactory<Dog> {
    @Override
    public Dog factory() {
        return new Dog();
    }
}
public static class AnimalFactory<T> implements IFactory<T> {
    private IFactory<T> tiFactory;
    public void setTiFactory(IFactory<T> tiFactory) {
        this.tiFactory = tiFactory;
    }
    @Override
    public T factory() {
        return tiFactory.factory();
    }
}
AnimalFactory<Dog> dogAnimalFactory = new AnimalFactory<>();
dogAnimalFactory.setTiFactory(new DogFactory());
Dog dog = dogAnimalFactory.factory();

通过设置一个代理的IFactory来实现AnimalFactory的多态性,避免通过泛型实例化对象

public static class AnimalClassFactory<T> {
    private Class<T> tClass;
    public void settClass(Class<T> tClass) {
        this.tClass = tClass;
    }
    public T factory() {
        try {
            return tClass.newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}
AnimalClassFactory<Dog> dogAnimalClassFactory = new AnimalClassFactory<>();
dogAnimalClassFactory.settClass(Dog.class);
Dog dog= dogAnimalClassFactory.factory();

这中方式看上去虽然更灵活,但是由于使用newInstance实例化,构造参数无法保证。

泛型数组

泛型数组是不可以直接创建的

//InstanceHolder<String>[] instanceHolders=new InstanceHolder<String>[4];//编译错误,
InstanceHolder<String>[] instanceHolders=new InstanceHolder[4];//有警告,unchecked assignment
instanceHolders[0]=new InstanceHolder<>("android");
String value=instanceHolders[0].getInstance();//通过泛型数组可以直接找到InstanceHolder的实际参数类型而不需要进行转换

范型数组基本使用

    public static class InstancesHolder<T>{
        private T[] instances;
        public InstancesHolder(int size) {
            instances = (T[]) new Object[size];//可以不加(T[])
        }
        public T get(int index) {
            return instances[index];
        }
        public void set(int index, T t) {
            instances[index] = t;
        }
        public T[] get() {
            return instances;
        }
    }
    InstancesHolder<String> stringInstanceHolder=new InstancesHolder<>(4);
    stringInstanceHolder.set(0,"android");
    System.out.println(stringInstanceHolder.get(0));//正常,输出android
    String[] strings=stringInstanceHolder.get();//报错,java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

上面示例中,我们使用Object创建一个数组,并强转为T[]类型,表面上看 我们的内部的数组似乎是String[]类型数组,但是当我们使用时,发现一旦引用为String[]时就会出现错误,这也从侧面的角度,泛型都是object类型,只是编译器内部在调用泛型变量或者示例时进行了默认的转换。 所以只要我们返回类型泛型数组,那它一定是object[]类型的数组。不过虽然是object类型数组,我们内部在使用它时,比如设置数据和调用数据时,可以直接使用泛型的数据或者方法。因为编译器默认做了强制转换。

    public static class InstancesHolder<T>{
        private Object[] instances;
        public InstancesHolder(int size) {
            instances = new Object[size];
        }
        public T get(int index) {
            return (T)instances[index];
        }
        public void set(int index, T t) {
            instances[index] = t;
        }
        public T[] get() {
            return (T[])instances;//这里会出现问题
        }
    }

上面的示例几乎可以说是编译器编译之后的版本。

    public static class InstancesClassHolder<T>{
        private T[] instances;
        public InstancesClassHolder(Class<T> clazz,int size) {
            instances= (T[]) Array.newInstance(clazz,size);
        }
        public T get(int index) {
            return instances[index];
        }
        public void set(int index, T t) {
            instances[index] = t;
        }
        public T[] get() {
            return instances;
        }
    }
    InstancesClassHolder<String> classHolder=new InstancesClassHolder<>(String.class,4);
    classHolder.set(0,"android");
    String[] strings=classHolder.get();//正常

这种方式获取的数组就是泛型对应的数组

总结

本文主要介绍了泛型最基本的使用方式和一些简单的技巧,下一章主要介绍的泛型的通配符,协变和逆协变方面的内容