Java编程思想—泛型(3)

Java编程思想—泛型(3)

泛型带来的各类问题

任何基本类型都不能作为类型参数

Java泛型的限制之一是不能讲基本类型作为类型参数。

解决方案是JDK的自动装箱拆箱机制

如;

public class ListOfInt {

    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < 5; i++) {
            list.add(i);
        }
        for (int i : list){
            System.out.println(i);
        }

    }
}


自动包装不能解决所有问题:

public class FArray {

    public static <T> T[] fill(T[] a, Generator<T> generator){
        for (int i = 0; i < a.length; i++) {
            a[i] = generator.next();
        }
        return a;
    }


}

public class PrimitiveGenericTest {

    public static void main(String[] args) {


        String[] strings = FArray.fill(
                new String[7],new RandomGenerator.String(10)
        );

        for (String s:strings){
            System.out.println(s);
        }

        Integer[] integers = FArray.fill(
                new Integer[7],new RandomGenerator.Integer()
        );

        for(int i:integers){
            System.out.println(i);
        }

//        自动装箱在这里不会起作用了
//        int[] b = FArray.fill(new int[7],new RandomGenerator());


    }
}


可以看到,自动包装机制不能用于数组

参数化接口

一个类不能实现同一个泛型接口的两种变体

public interface Apple<T> {
}

public class ReadF implements Apple<ReadF>{
}
//public class ReadF2 extends ReadF implements Apple<ReadF2>{
//error
//}

ReadF2无法编译,因为擦差会将Apple< ReadF >与Apple< Read2F >简化为一样的类Apple,上面的代码就意味着重复两次实现相同的接口。

转型与警告

使用带有泛型类型参数的转型或instanceof不会有任何效果

public class FixedSizeStack<T> {

    private int index = 0;

    private Object[] storage;

    public FixedSizeStack(int size) {
        storage = new Object[size];
    }

    public void push(T item){
        storage[index++] = item;
    }

    public T pop(){
        return (T) storage[--index];
    }



}


public class GenericCast {

    public static final int size = 10;

    public static void main(String[] args) {

        FixedSizeStack<String> fixedSizeStack =
                new FixedSizeStack<>(size);

        for(String s : "A B J J I D U K L P".split(" ")){
            fixedSizeStack.push(s);
        }

        for (int i = 0; i < size; i++) {
            String s = fixedSizeStack.pop();
            System.out.println(s +"     ");
        }

    }
}

编译器会对

return (T) storage[--index];

发出警告,这是因为擦除的原因,编译器无法知道这个转型是否是安全的,并且pop实际上没有执行任何转型

重载

public class UseList<P,Q> {

//    void f(List<P> p){
//
//    }
//
//    void f(List<Q> q){
//
//    }


}


上面例子由于擦除的原因,重载方法将产生相同的类型签名

自限定的类型

令人费解的用法:

public class SelfBounded<T extends SelfBounded<T>> {
}

SelfBounded类接受泛型参数T,而T由一个边界类限定,这个边界就是拥有T作为其参数的SelfBounded

例子:

public class BasicHolder<T> {

    T element;

    public void setElement(T element) {
        this.element = element;
    }

    public T getElement() {
        return element;
    }

    void f(){
        System.out.println(element.getClass().getSimpleName());
    }

}

public class Subtype extends BasicHolder<Subtype>{


}



public class CRGWithBasicHolder {

    public static void main(String[] args) {

        Subtype subtype = new Subtype();

        Subtype subtype2 = new Subtype();

        subtype.setElement(subtype2);

        Subtype subtype3 = subtype.getElement();

        subtype.f();

    }
}


细节:

新类Subtype接受的参数和返回的值具有Subtype类型而不仅仅是基类BasicHolder类型,这就是CRG的本质:基类用导出类替代其参数

BasicHolder可以使用任何类型作为其泛型参数

public class Other {
}


public class BasicOther extends BasicHolder<Other>{


}

public class UnconStrained {

    public static void main(String[] args) {

        BasicOther b = new BasicOther();

        BasicOther b2 = new BasicOther();

        b.setElement(new Other());

        Other other = b.getElement();

        b.f();

    }
}



参数协变

自限定类型的价值在于它们可以产生协变参数类型—方法参数类型会随子类而变化

public class Apple extends Fruit{
    @Override
    public String toString() {
        return "Apple";
    }
}

public class Fruit {
}

public interface GetApple extends GetFruit{

    Apple get();
}


public interface GetFruit {

    Fruit get();
}


public class Test {

    static void test(GetApple getApple){
        Apple apple = getApple.get();
    }

    public static void main(String[] args) {

    }
}


GetApple中的get方法覆盖了GetFruit中的get(),并且返回了一个从GetFruit.get()的返回类型中导出的类型。

混型

public interface Fly {

    void fly();
}


public class Swing implements Fly{

    @Override
    public void fly() {
        System.out.println("I can Fly");
    }
}



public class Duck implements Fly{

    private Swing swing = new Swing();

    @Override
    public void fly() {
        swing.fly();
    }

    void swim(){
        System.out.println("I can swim");
    }
}

但是当使用更复杂的模型时,代码数量会急剧增加

装饰器

public interface Basket {

    void show();
}

public class Original implements Basket{

    @Override
    public void show() {
        System.out.println("I am Basket");
    }
}

public class AppleDecorator implements Basket{

    private Basket basket;

    public AppleDecorator(Basket basket) {
        this.basket = basket;
    }

    @Override
    public void show() {

        basket.show();
        System.out.println("APPLE");
    }
}

public class BananaDecorator implements Basket{

    private Basket basket;


    public BananaDecorator(Basket basket) {
        this.basket = basket;
    }

    @Override
    public void show() {
        basket.show();
        System.out.println("BANANA");
    }
}


public class Main {

    public static void main(String[] args) {

        Original original = new Original();

        Basket basket =
                new AppleDecorator(new BananaDecorator(original));

        basket.show();


    }
}


潜在类型机制

在C++和python中,可以不知道当前类的类型,就可以调用方法

Java如何进行补偿呢?

反射

public class TypeOne {

    private int data = 1;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }
}

public class TypeTwo {

    private int data = 2;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }
}



public class Main1 {

    public static void getData(Object type){
        Class<?> c = type.getClass();

        try {
            Method method = c.getMethod("getData");

            int data = (int) method.invoke(type,null);

            System.out.println(type.getClass().getSimpleName()+"  data:"+data);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {

        TypeOne one = new TypeOne();

        TypeTwo typeTwo = new TypeTwo();

        getData(one);

        getData(typeTwo);

    }
}


将一个方法用于序列

public class Full {

    public static <T,S extends Iterable<T>> void fill(
            S s, Method method,Object...args
            ){
        Iterator<T> iterator = s.iterator();

        while (iterator.hasNext()){
            T t = iterator.next();
            try {
                method.invoke(t,args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }


    }
}


public class Apple {
    private static int count = 1;
    private final int id = count++;

    public void getId(){
        System.out.println(id);
    }
}


public class Main2 {

    public static void main(String[] args) {

        ArrayList<Apple> list = new ArrayList<>();
        for(int i=0; i<10; ++i){
            list.add(new Apple());
        }
        try {
            fill(list, Apple.class.getMethod("getId"),null);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }

    }
}


发布了189 篇原创文章 · 获赞 58 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/Coder_py/article/details/103908882