java入门学习记录day2 - 泛型

77 阅读4分钟

泛类

泛型类

public class Container<K, V> {
    private K key;
    private V value;

    public Container(K k, V v) {
        key = k;
        value = v;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }
}
public class Main {
    public static void main(String[] args) {
        Container<String, String> c1 = new Container<String, String>("name", "findingsea");
        Container<String, Integer> c2 = new Container<String, Integer>("age", 24);
        Container<Double, Double> c3 = new Container<Double, Double>(1.1, 2.2);
        System.out.println(c1.getKey() + " : " + c1.getValue()); // name : findingsea
        System.out.println(c2.getKey() + " : " + c2.getValue()); // age : 24
        System.out.println(c3.getKey() + " : " + c3.getValue()); // 1.1 : 2.2
    }
}

泛型接口

Generator规定了需要有一个next方法,这个方法的返回类也是根据传入的类型而定的

public interface Generator<T> {
    public T next();
}

FruitGenerator实现了Generator接口

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

调用

public class Main {
    public static void main(String[] args) {
        FruitGenerator generator = new FruitGenerator();
        System.out.println(generator.next());
        System.out.println(generator.next());
        System.out.println(generator.next());
        System.out.println(generator.next());
    }
}

输出每次都随机打印Apple、Banana、Pear中的一个

泛型方法

泛型方法

public class Main {
    public static <T> void out(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        out("findingsea");
        out(123);
        out(11.11);
        out(true);
    }
}

输出

findingsea
123
11.11
true

这里用到了可变参数

public class Main {
    public static <T> void out(T... args) {
        for (T t : args) {
            System.out.println(t);
        }
    }

    public static void main(String[] args) {
        out("findingsea", 123, 11.11, true);
    }
}

泛型参数的界限

通过方式规定泛型的界限

public class NumberTest<T extends Integer> {
    private T num;

    public NumberTest(T num) {
        this.num = num;
    }

    public boolean isOdd() {
        return num.intValue() % 2 == 1;
    }
}

调用

public class Main {
    public static void main(String[] args) {
        NumberTest<Integer> nt = new NumberTest<>(123);
    }
}

接着引入下一个问题,如何为泛型参数添加多个限制范围,多重限制范围格式如下:

<T extends A & B & C>

一个泛型参数可以有多重限制范围,使用“&”分隔。且限制范围中之多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。举例如下:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

注意A,B,C要放在D前面,否则无法通过编译

泛型方法与泛参界限的组合

如果说泛型方法是一个有用的工具,那泛参的界限就应该这个工具的灵魂,为这个工具添加了一些“行为准则”。如下:设计一个方法,统计在一个数组里比指定元素大的个数,

public class Main {
    public static <T> int countGreater(T[] array,T elem) {
        int count = 0;
        for (T t : array) {
            if (t > elem) {// Error, Operator can not applied to 'T', 'T'
                ++count;
            }
        }
        return count;
    }
}

发现上面这个方法无法通过编译,为什么呢??因为操作符“>”只可以用在基本数据类型(byte,char,short,int,float,long,double,boolean),却不可以用来比较类对象之间的大小(除非实现了Comparable接口)。想要解决这个矛盾,就需要为添加一个界限Comparable:

public interface Comparable<T> {
    public int compareTo(T o);
}

然后将Comparable定为泛型的界限,再用compareTo替代">"运算符

public class Main {
    public static <T extends Comparable<T>> int countGreater(T[] array,T elem) {
        int count = 0;
        for (T t : array) {
            if (t.compareTo(elem) > 0) {//无编译错误
                ++count;
            }
        }
        return count;
    }
}

泛型、继承与子类型

这种“is-a”关系,同样也是用泛型。如果你将泛参设置Number,那么在随后的调用里,只需要传入一个数据对象就行了,如下:

Box<Number> box = new Box<>();
box.add(new Integer(1));
box.add(new Double(1.0));

但是方法中的Number泛型类却不能接受Integer和Double的参数

public void someMethod(Box<Number> n) { /*.....*/}

因为Box与Box都不是Box的子类。

泛型类与子类型

泛型的“extends”与继承的“extends”并不一样,泛型的“extends”其后可以是一个类(如T extends Number),同样也可以是一个接口(如T extends List)。泛型的”extends“代表子类型,而不是子类。

在泛型里,也存在子类型,前提是其泛型参数的限制并没有改变,可以认为泛参没有改变,其实就是从原来的类或接口来判断泛型的子类型。为了形象理解,我们已collection类来作个例子,如:ArrayList implement List,而List extends Collection,那么ArrayList就是List的子类型,而List则是Collection,其关系图如下:

深入一点来谈,现在假设需要定义自己的List接口 — PayLoadList,其定义如下:

interface PayloadList<E,P> extends List<E> {
void setPayload(int index, P val);
//...
}

如上,则下面的样例都是List子类型,:

PayloadList<String,String>
PayloadList<String,Integer>
PayloadList<String,Exception>

参考资源:
https://juejin.cn/post/6844903456621199367
https://blog.51cto.com/peiquan/1302898