java 泛型

1.什么是泛型

泛型就是参数化类型,使用该特性创建的接口、类、方法。可以作为参数指定操作的数据类型

泛型解决了以前的类型安全及显示的在Object及实际类型之间的转换。

public class Gen <T> {
    private T ob;

    public Gen (T go) {
        this.ob = go;
    }

}

其中:T是类型参数的名称。因此,Gen是泛型类,也称为参数化类型

1.2带两个类型参数的泛型类
public class Gen <T,V> {
    private T ob;

    public Gen (T go) {
        this.ob = go;
    }
}
1.3 泛型有界类型

假设希望创建一个泛型类,类中包含一个求数组中平均值的方法。包括整数、浮点数以及双精度浮点数。

public class Stats<T extends Number> {
    T[] nums;

    public Stats(T[] nums) {
        this.nums = nums;
    }

    public double average() {
        double sum = 0.0;
        for (T t:nums) {
            sum = t.doubleValue() + sum;
        }
        return sum / nums.length;
    }
}
public class StatsDemo {
    public static void main(String[] args) {
        Integer[] nums = {1,2,3,4,5};
        Stats<Integer> stats = new Stats<>(nums);
        System.out.println(stats.average());

        Double[] dnums = {1.0,1.2,1.3,1.4};
        Stats<Double> doubleStats = new Stats<>(dnums);
        System.out.println(doubleStats.average());

        //编译错误
//        String[] strStats = {"1","2"};
//        Stats<String> stringStatus = new Stats<String>(strStats);

    }
}

可以指定多个接口作为边界
指定多个接口作为边界时使用&链接。一个类可以实现多个接口,但是只能继承一个类

public class Stats<T extends Number & Comparable> {
}
1.4泛型通配符参数

假如对于上面的Stats类要添加一个判断两个对象平均值相同的方法。而不考虑每个对象的具体的数据类型。
实现sameAge方法的一种方式是传递Stats参数。然后根据调用对象比较参数的平均值。

错误的实现方式

public class Stats<T extends Number & Comparable> {


    public boolean sameAve(Stats<T> stats){
        if (average() == stats.average()) {
            return true;
        }
        return false;
    }
}
public class StatsDemo {
    public static void main(String[] args) {
        Integer[] nums = {1,2,3,4,5};
        Stats<Integer> stats = new Stats<>(nums);


        Double[] dnums = {1.0,1.2,1.3,1.4};
        Stats<Double> doubleStats = new Stats<>(dnums);

        //编译错误。因为数据类型不相同
        System.out.println(stats.sameAve(doubleStats));
    }
}

这种尝试存在的问题是 只有当Stats的类型和调用对象的类型相同时才能工作。

正确的做法:
使用通配符参数,由”?”指定,表示未知的类型。

public class Stats<T extends Number & Comparable> {

    public boolean sameAve(Stats<?> stats){
        if (average() == stats.average()) {
            return true;
        }
        return false;
    }
}
1.5 有界通配符

为了理解为什么需要有界通配符,从以下例子开始:

//两维数组类
public class TwoD {
    int x;
    int y;

    TwoD(int x,int y) {
        this.x = x;
        this.y = y;
    }
}

//三维数组类
public class ThreeD extends TwoD {
    int z;
    ThreeD(int x, int y,int z) {
        super(x, y);
        this.z = z;
    }
}

//四维数组类
public class FourD extends ThreeD {
    int t;
    FourD(int x, int y, int z,int t) {
        super(x, y, z);
        this.t = t;
    }
}

上面几个类的顶部类都是TwoD,因此可以搞一个泛型类:

public class Coords<T extends TwoD> {
    T[] coords;

    public Coords(T[] coords) {
        this.coords = coords;
    }

}
//Coords制定了一个由TwoD界定的类型参数。这意味着在Coords对象中存储的所有数组将包含TwoD类或其子类的对象。

假设我们现在要编写一个方法,要显示数组中每个元素的X,Y的坐标。

public   void showXY(Coords<?> c) {
    for (int i = 0;i < c.coords.length) {
        System.out.println(c.coords[i].x + c.coords[i].y);
    }
}

假设我们现在要编写一个方法,要显示数组中每个元素的X,Y,Z的坐标。
这时不能直接使用通配符,因为TwoD没有Z
此时需要使用有界通配符。对传入的参数类型做限制。

public   void showXYZ(Coords<? extends ThreeD> c) {
    for (int i = 0;i < c.coords.length;i++) {
        System.out.println(c.coords[i].x + c.coords[i].y + c.coords[i].z);
    }
}

// ?表示任意类型,只要这些类型为ThreeD或者ThreeD的派生类。

有界通配符为类型参数可以指定上界或下界,从而限制方法可以操作的对象类型。最常用的有界统配符是上界,使用extends子句创建。
上界语法:

public   void showXYZ(Coords<? super FourD> c) {
    for (int i = 0;i < c.coords.length;i++) {
        System.out.println(c.coords[i].x + c.coords[i].y );
    }
}
//注意 此时的方法类型参数是FourD的超类,如果FourD本身的超类是Object,那么参数类型就是他本身。

2.泛型方法

语法:

ret-type meth-name(param-list) {…}

举例:

public class GenMathDemo {

    //静态方法
    static <T extends Comparable<T>,V extends T> boolean isIn(T[] ts,V v) {
        for (T t:ts) {
            if (v.equals(t)) {
                return true;
            }
        }
        return false;
    }

    //实例方法
    <T extends Comparable<T>,V extends T> boolean isInTest(T[] ts,V v) {
        for (T t:ts) {
            if (v.equals(t)) {
                return true;
            }
        }
        return false;
    }
}

static

3.泛型构造参数

可以将构造参数泛型化,即使他们的类不是泛型类。

public class GenCons {
    private double val;

    public <T extends Number> GenCons(T args) {
        val = args.doubleValue();
    }

    public void showVal() {
        System.out.println("arg val:" + val);
    }

    public static void main(String[] args) {
        GenCons genCons = new GenCons(123);
        GenCons test2 = new GenCons(125.4F);

        genCons.showVal();
        test2.showVal();
    }
}

4.泛型接口

除了可以定义泛型类和泛型方法外,还可以定义泛型接口。泛型接口的定义和泛型类相似。

泛型接口的通用语法:
interface interface-name {..//}

泛型接口的实现
class class-name implements interface-name

泛型接口定义:

public interface MinMax<T extends Comparable<T>> {
    T min();
    T max();
}

泛型接口实现:

public class MyClass<T extends Comparable<T>> implements MinMax<T> {
    @Override
    public T min() {
        return null;
    }

    @Override
    public T max() {
        return null;
    }
}

public class MyClass

1、java反射中的泛型API

Type是所有泛型的接口
这里写图片描述

1.1 参数化类型接口
public interface ParameterizedType extends Type {
}

如泛型:List 代表参数化类型
java中目前并没有提供直接获取一个类的参数化类型。但是提供了获取父类的泛型化参数:

** 
 * Represents a generic type {@code T}. Java doesn't yet provide a way to
 * represent generic types, so this class does. Forces clients to create a
 * subclass of this class which enables retrieval the type information even at
 * runtime.
 *
 * <pre>
 * TypeReference<List<String>>; list = new TypeReference<List<String>>() {};
 * </pre>
 */
public class TypeReference<T> {
    /**
     * Constructs a new type literal. Derives represented class from type
     * parameter.
     *
     * <p>Clients create an empty anonymous subclass. Doing so embeds the type
     * parameter in the anonymous class's type hierarchy so we can reconstitute it
     * at runtime despite erasure.
     */
    protected TypeReference(){

        /*获取泛型化父类*/
        Type superClass = getClass().getGenericSuperclass();

        /*获取泛型化参数的实际类型*/
        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

        Type cachedType = classTypeCache.get(type);
        if (cachedType == null) {
            classTypeCache.putIfAbsent(type, type);
            cachedType = classTypeCache.get(type);
        }

        this.type = cachedType;
    }
1.2 通配符类型
public class GenDemo {
    public static void main(String[] args) throws NoSuchMethodException {
        ThreeD[] threeDS = {new ThreeD(1,2,3)};
        Coords coords = new Coords<ThreeD>(threeDS);
        Method method = coords.getClass().getMethod("showXY",Coords.class);
        ParameterizedType type1 = (ParameterizedType) method.getGenericParameterTypes()[0];
        Type type = type1.getActualTypeArguments()[0];
        if (type instanceof WildcardType) {
            WildcardType wildcardType = (WildcardType) type;
            System.out.println(wildcardType.getTypeName());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值