一个你读框架源码之前必须要理解的技术点,泛型(一)

泛型

泛型(Generic)是Java编程语言的强大功能。
泛型的本质是参数化类型,在不创建新的类型的情况下,通过泛型指定某一个类、方法或接口成不同类型。将类型由原来的具体类参数化,此时的参数可以称为类型形参

public class Test1 {
	/**
	 * @param args
	 * ArrayList<Integer>,ArrayList被指定了一个Integer的类型形参,所以此时的ArrayList只能添加Integer的对象
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(111);
		list.add(222);
		System.out.println(list);
	}
}

泛型的优点

1.在编译时进行更强的类型检查。Java编译器将强类型检查应用于通用代码,如果代码违反类型安
全,则会发出错误。修复编译时错误比修复运行时错误容易,后者可能很难找到。
2.消除类型转换
3.可以帮助我们写出更加通用的代码

泛型擦除

java中的泛型 只是在编译期做语法检查 对你进行限制
泛型只在编译期有效(class).运行期没有泛型,泛型就被擦除了,所有类型都变成了相同的基本类型

public class Test1 {

	/**
	 * @param args
	 * ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现呢?
	 * 泛型只在编译期有效,在运行期会被擦除掉
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(111);
		list.add(222);
		Class clazz = Class.forName("java.util.ArrayList");				//获取字节码对象
		Method m = clazz.getMethod("add", Object.class);				//获取add方法,add方法中可以放任意对象所以参数是Object.class
		m.invoke(list, "abc");
		System.out.println(list);
	}
}

泛型类

泛型类型修饰类为泛型类,例如List
1.泛型类可以由多个泛型参数例如Class<T,R>
2.泛型参数如果想使用基本数据类型的话必须传递其包装类,举例:int->Integer
3.如果不给泛型类指定具体类型,此时泛型类型可以为任意类型

public class ExampleUnitTest {
    @Test
    public void exampleTest() {
        TestClass<Integer> object1 = new TestClass<>(1);//泛型是Integer类型,int的包装类
        TestClass<String> object2 = new TestClass<>("2");//泛型是String类型
        TestClass object3 = new TestClass();//如果不给泛型类指定具体类型,类中使用泛型的成员可以为任意类型
        object3.setMember(123);
        System.out.println(object3.member);
        object3.setMember("123");//
        System.out.println("exampleTest: " + object1.getMember() + " , " + object2.getMember() + " , " + object3.getMember());
    }

    public class TestClass<T> {
        private T member;

        public TestClass() {

        }

        public TestClass(T member) {
            this.member = member;
        }

        public T getMember() {
            return member;
        }

        public void setMember(T member) {
            this.member = member;
        }
    }
}

输出

123
exampleTest: 1 , 2 , 123

泛型接口

泛型修饰接口
泛型接口的实现类未传入具体的泛型实参,此时应该将泛型同样声明到类中,否者使用的时候无法确定具体类型

public interface TestInterface1<R> {
    void callback(R r);
}
public class ExampleClass4<R> implements TestInterface1<R> {
    //泛型接口的实现类未传入具体的泛型,举例<T>,此时应该将泛型同样声明到类中
    @Override
    public void callback(R r) {
        System.out.println(r);
    }
}
public class ExampleUnitTest {
    @Test
    public void exampleTest2(){
        ExampleClass4<String> exampleClass4 = new ExampleClass4<>();
        exampleClass4.callback("String类型");
    }
}

打印

String类型

泛型接口的实现类传入具体的泛型实参,举例 < String>,使用对应泛型的地方都应该为对应类型

public class ExampleClass5 implements TestInterface1<String> {
    @Override
    public void callback(String s) {
        System.out.println(s);
    }
}
public class ExampleUnitTest {
    @Test
    public void exampleTest2(){
        ExampleClass5 exampleClass5 = new ExampleClass5();
        exampleClass5.callback("exampleClass5");
    }

}

打印

exampleClass5

如果泛型接口有两个泛型参数的时候,当泛型接口的实现类传入了具体的泛型实参,情况和一个泛型参数的一样,使用对应泛型的参数或成员都应该为对应类型
这里注意的是当泛型接口的实现类没有传入具体的泛型实参的情况

泛型接口的实现类未传入具体的泛型,举例<T,R>,此时应该将泛型同样声明到类中
alt+enter默认会在ExampleClass2声明第一个泛型T,如果想要使用泛型R,也需要在ExampleClass2类上声名,否者会报错,见以下例子

public interface TestInterface2<T, R> {
    void callback(T t);
    R onNext();
}
public class ExampleClass2<T> implements TestInterface2<T, R> {
    //泛型接口的实现类未传入具体的泛型,举例<T,R>,此时应该将泛型同样声明到类中
    //alt+enter默认会在ExampleClass2声明第一个泛型T,如果想要使用泛型R,也需要在ExampleClass2类上声名

    private R member;

    @Override
    public void callback(T t) {
        System.out.println(t);
    }

    @Override
    public R onNext() {
        return getMember();
    }

    private R getMember() {
        return member;
    }

    public void setMember(R member) {
        this.member = member;
    }

}

具体使用的时候会报错cannot be applied to …
在这里插入图片描述
所以如果外部需要使用到第二个泛型参数,同样也需要声明到类上

public class ExampleClass3<T,R> implements TestInterface2<T, R> {
    //泛型接口的实现类未传入具体的泛型,举例<T,R>,此时应该将泛型同样声明到类中
    private R member;

    @Override
    public void callback(T t) {
        System.out.println(t);
    }

    @Override
    public R onNext() {
        return getMember();
    }

    private R getMember() {
        return member;
    }

    public void setMember(R member) {
        this.member = member;
    }

}
public class ExampleUnitTest {
    @Test
    public void exampleTest2(){
        ExampleClass3<String, Boolean> exampleClass3 = new ExampleClass3<>();
        exampleClass3.callback("传递String");
        exampleClass3.setMember(true);
        Boolean aBoolean = exampleClass3.onNext();
    }
}

泛型方法

1.在访问权限和返回值类型之间声明方法的泛型,注意,这个和泛型类 TestMethod的T不是同一个,可以是相同类型也可以是不同类型,当然中的T可以换成任意字母
2.声明了泛型的方法才是泛型方法,仅在方法中,没有声明的不是泛型方法,比如当前类的构造函数public TestMethod(T t){},就不是泛型方法
3.泛型方法中声明的泛型,可以在方法中使用该泛型,反之不能使用该泛型
例子:

public class TestMethod<T> {
    private T mT;
    public TestMethod(T t) {
        this.mT = t;
    }

    public T getmT() {
        return mT;
    }

    /**
     * 泛型方法
     * 1.在访问权限和返回值类型之间声明方法的泛型<T>,注意,这个<T>和泛型类 TestMethod<T>的T不是同一个,可以是相同类型也可以是不同类型,取决于泛型实参传的是什么类型,可以见例子ExampleUnitTest,当然<T>中的T可以换成任意字母
     * 2.声明了泛型<T>的方法才是泛型方法,仅在方法中,没有声明的不是泛型方法,比如当前类的构造函数public TestMethod(T t){},就不是泛型方法
     * 3.泛型方法中声明的泛型<T>,可以在方法中使用该泛型,反之不能使用该泛型
     * @param t
     * @param <T>
     */
    public <T> void myMethod1(TestMethod<T> t){
        T t1 = t.getmT();
        System.out.println(t1+","+t1.getClass().toString());
    }

    /**
     * 不是泛型方法,只是在参数中使用了类中声明的泛型T,此处如果调用该方法,只能传和类一致的泛型
     * @param t
     */
    public void myMethod2(TestMethod<T> t){
        T t1 = t.getmT();
        System.out.println(t1+","+t1.getClass().toString());
    }
}
public class ExampleUnitTest {
    @Test
    public void test() {
        TestMethod<String> testMethod1 = new TestMethod<>("泛型方法");
        TestMethod<Integer> testMethod2 = new TestMethod<>(123);
        /**
         * 下面方法调用正常
         * 因为在方法中声明了新泛型,可以在方法中使用
         */
        testMethod1.myMethod1(testMethod1);
        /**
         * 下面报错:TestMethod<String> in TestMethod cannot be applied to TestMethod<Integer>
         * 因为myMethod2使用的类中声明的泛型,当前是String类型的,而传入Integer,所以报错
         */
        testMethod1.myMethod2(testMethod2);
    }

}

注释掉testMethod1.myMethod2(testMethod2);,运行

泛型方法,class java.lang.String

注意:如果在静态方法中使用泛型,这个静态方法必须设置成静态方法

泛型通配符

<?>

可以传入任意类型,例如在上一节泛型方法中举的例子,在myMethod2中使用的是类中的泛型,对象testMethod1对应类中的泛型是String,而想传入泛型是Integer的对象,在不是泛型方法的情况下是行不通的,这里如果我们在方法参数上使用泛型通配符,就可以解决这个问题
例子:

    /**
     * 泛型通配符,可以传入任意类型
     * @param t
     */
    public void myMethod3(TestMethod<?> t){
        Object o = t.getmT();
        System.out.println(o+" , "+o.getClass().toString());
    }
    @Test
    public void test() {
        TestMethod<String> testMethod1 = new TestMethod<>("泛型方法");
        TestMethod<Integer> testMethod2 = new TestMethod<>(123);
        /**
         * 下面方法调用正常
         * 因为在方法中声明了新泛型,可以在方法中使用
         */
//        testMethod1.myMethod1(testMethod1);
        /**
         * 下面报错:TestMethod<String> in TestMethod cannot be applied to TestMethod<Integer>
         * 因为myMethod2使用的类中声明的泛型,当前是String类型的,而传入Integer,所以报错
         */
//        testMethod1.myMethod2(testMethod2);
        testMethod1.myMethod3(testMethod2);
    }

输出

123 , class java.lang.Integer

泛型上下边界

在使用泛型的时候,可以限制泛型实参的上下边界,也就是:

指定泛型的父类是某种类型就是限制泛型的上边界,只能使用上边界类型和上边界内的类型,例如< ? extends TestClass1>,上边界是TestClass1,可以使用泛型TestClass1和TestClass1的子类泛型;

指定泛型的子类是某种类型就是限制泛型的下边界,只能使用下边界类型的泛型和下边界以内的泛型,例如< ? super TestClass2 > 下边界是TestClass2,只能使用泛型为TestClass2和TestClass2的父类泛型

举例
水果、苹果、绿苹果的例子,水果是超类,苹果继承水果,绿苹果继承苹果;
定义一个泛型容器Generic,
eat2(Generic<? extends Apple> t) 方法是确定了上边界为Apple,所以只能传的泛型为Apple或者GreenApple
eat3(Generic<? super Apple> t) 方法是确定了下边界为Apple,所以只能传的泛型为Apple或者Friut

public class Fruit {
    private String name;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Apple extends Fruit {
    public Apple() {
    }

    public Apple(String name) {
        super(name);
    }

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

    public GreenApple(String name) {
        super(name);
    }

    @Override
    public String toString() {
        return "GreenApple{}";
    }
}
public class Generic<T> {
    private T t;

    public Generic() {
    }

    public Generic(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }


    public String eat2(Generic<? extends Apple> t){

        return t.getT().toString();
    }

    public String eat3(Generic<? super Apple> t){

        return t.getT().toString();
    }
}
@Test
    public void exampleTest3() {
        Generic<Fruit> fruitGeneric = new Generic<>();
        Generic<Apple> appleGeneric = new Generic<>();
        Generic<GreenApple> greenAppleGeneric = new Generic<>();
        Fruit fruit = new Fruit("水果");
        Apple apple = new Apple("苹果");
        GreenApple greenApple = new GreenApple("绿苹果");
        fruitGeneric.setT(fruit);
        appleGeneric.setT(apple);
        greenAppleGeneric.setT(greenApple);

        //Apple是上边界 可以传Generic中泛型继承于Apple的子类以及Apple
        System.out.println(appleGeneric.eat2(greenAppleGeneric));
        System.out.println(appleGeneric.eat2(appleGeneric));
        
        //Apple是下边界 可以传Generic中Apple泛型的父类以及Apple
        System.out.println(appleGeneric.eat3(fruitGeneric));
        System.out.println(appleGeneric.eat3(appleGeneric));
    }

打印

GreenApple{}
Apple{}
com.himi.test.Fruit@3b95a09c
Apple{}

结束语

先写一下泛型基本使用,过些日子写一下其它用法和注意事项

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章