java泛型

 

泛型作用:
安全:在编译的时候检查类型安全;
省心:所有的强制转换都是自动和隐式的,提高代码的重用率;
 
1.格式:      
class 类型<字母列表>{
修饰符 构造器(字母);
修饰符 返回类型 方法(字母)
}
泛型常见字母:
T     Type 表示类型
K V    分别代表键值中的Key和Value
E     代表Element
?      表示不确定的类型
 
注:泛型声明时,字母不能使用在静态属性、静态方法上;
因为泛型是在使用的时候确定的,静态属性、静态方法编译的时候就确定;
 
2.使用      
使用时指定具体的类型,(不能指定基本类型,而是用引用类型)
1)编译时会进行类型检查;
2)获取数据时不需要进行类型转换
 
 
例子1:
package com.cy.gen;

/**
 * 泛型类:声明时使用泛型
 * 
 */
public class Student<T1, T2> {
    private T1 javaScore;
    private T2 oracleScore;
    
    //private static T1 phpScore;
    
    public T1 getJavaScore() {
        return javaScore;
    }
    public void setJavaScore(T1 javaScore) {
        this.javaScore = javaScore;
    }
    public T2 getOracleScore() {
        return oracleScore;
    }
    public void setOracleScore(T2 oracleScore) {
        this.oracleScore = oracleScore;
    }
    
    public static void main(String[] args) {
        Student<String, Integer> stu = new Student<String, Integer>();     //使用时指定类型,引用类型
        stu.setJavaScore("优秀");                                        //类型检查
        int oracleScore = stu.getOracleScore();                            //自动类型转换
        
    }
    
}
View Code
 
例子2:
查看代码
 package com.generic;

import java.util.List;

/**
 * 类级别的泛型
 */
public class GenericDao<T> {

    public void add(T t) {

    }

    /**
     * 查单个
     */
    public T findById(int id) {
        return null;
    }

    /**
     * 查集合:根据条件查询返回集合
     */
    public List<T> findByConditions(String where) {
        return null;
    }

    public void delete(T t) {

    }

    /**
     * 注意:这里编译器报错了,不能这么写。
     * 当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和静态方法调用。
     * 因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
     *
     * 大白话解释:GenericDao对象里面的参数类型是T,静态方法不用创建对象就可以直接调用,对象
     * 都没创建里面的参数类型怎么会知道呢?
     *
     * 要硬写的话只能再void前写T,但是这个T和类中的泛型T就没关系了,是方法自己的,独立的。
     */
    public static void update(T t) {

    }
}
 
例子2,泛型接口:
package com.cy.gen;

public interface Comparator<T> {
    
    void compare(T t);
}
View Code
 
例子3:泛型方法:
修饰符 <字母> 返回类型 方法名(字母){
}
要定义泛型方法,只需将泛型参数列表置返回值前;
注:泛型还可以定义在方法中,是否拥有泛型方法,与其所在的类是否泛型没有关系。
package com.cy.gen;

import java.io.Closeable;
import java.io.IOException;

/**
 * 泛型方法 <> 返回类型前面
 * 只能访问对象的信息,不能修改信息
 */
public class TestMethod {
    public static void main(String[] args) {
        test("b");    //T --> String
        test(1);
    }
    
    //泛型方法
    public static <T> void test(T t){
        //t.setXXX();
        System.out.println(t);
    }
    
    //extends <=
    //T是Closeable接口的实现类   ...代表可变参数
    public static <T extends Closeable> void test(T... a){
        for(T temp: a){
            try {
                if(temp!=null) temp.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

泛型用在方法中,extends,super的例子使用:

查看代码

package com.generic;

import cn.hutool.core.collection.CollectionUtil;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class GenericTest1 {

    /**
     * 泛型 extends 泛型 super
     * 其实解决的就是一个参数范围,向下与向上
     */
    public static void testWithGenericRange() {
        List<Double> list1 = new ArrayList<>();
        sort(list1);

        List<Double> list2 = new ArrayList<>();
        list2.add(2D);
        list2.add(3D);

        List<Integer> list3 = new ArrayList<>();
        list3.add(2);
        list3.add(3);

        Filter<Number> filter = o -> o.doubleValue() > 0D;

        removeIf(list2, filter);
        removeIf(list3, filter);
    }

    public static void sort(List<? extends Number> list) {
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        CollectionUtil.sort(list, Comparator.comparingInt(Number::intValue));
    }

    interface Filter<E> {

        boolean test(E e);
    }

    /**
     *  修饰符 <字母> 返回类型 方法名(字母)
     *  public static: 修饰符
     *  <E>:字母
     *  List<E>: 返回类型
     *  removeIf:方法名
     */
    public static <E> List<E> removeIf(List<E> list, Filter<? super E> filter) {
        List<E> toRemove = new ArrayList<>();
        for (E e: list) {
            if (filter.test(e)) {
                toRemove.add(e);
            }
        }
        list.removeAll(toRemove);
        return toRemove;
    }
}

 

有界限的泛型:

有界限的泛型写法:
 package com.generic;


public class GenericTest3 {

    public static void main(String[] args) {
        Print<Car> p1 = new Print<>(new Car());
        p1.print();

        Print<Bus> p2 = new Print<>(new Bus());
        p2.print();
    }

}

/**
 * 有界限的泛型
 * T extends Vehicle & Thing : 传入的参数必须是Vehicle的子类,而且实现了Thing接口
 * 写法是:T extends class & interface
 */
class Print<T extends Vehicle & Thing> {
    private T content;

    Print(T content) {
        this.content = content;
    }

    public void print() {
        System.out.println("品牌:" + content.getBrand());
        System.out.println(content);
    }
}

class Vehicle implements Thing{

    String brand;
    String color;

    @Override
    public String getBrand() {
        return this.brand;
    }

    @Override
    public String getColor() {
        return this.color;
    }
}

class Bus extends Vehicle {

}

class Car extends Vehicle {

}

interface Thing {
    String getBrand();
    String getColor();
}

 

 

 3.派生子类        

属性类型

方法重写

泛型擦除

例子1:父类为泛型:

package com.cy.gen02;

/**
 * 父类为泛型类
 * 1.属性
 * 2.方法
 * 
 * 要么同时擦除,要么子类大于等于父类的类型
 */
public abstract class Father<T, T1> {
    T name;
    public abstract void test(T t); 
    
}

/**
 * 子类声明时指定具体类型
 */
class Child1 extends Father<String, Integer>{
    @Override
    public void test(String t) {
    }
}

/**
 * 子类为泛型类,类型在使用时确定
 */
class Child2<T, T1> extends Father<T, T1>{
    @Override
    public void test(T t) {
    }
}

/**
 * 子类为泛型类,父类不指定类型,泛型的擦除,使用Object替换
 */
class Child3<T1, T2> extends Father{
    
    @Override
    public void test(Object t) {
        
    }
}
View Code

例子2:泛型接口:

package com.cy.gen02;

/**
 * 泛型接口: 与继承同理
 * 重写方法随父类而定
 * @author CY
 *
 * @param <T>
 */
public interface Comparable<T> {
    void compare(T t);
}
//声明子类指定具体类型
class Comp implements Comparable<String>{
    @Override
    public void compare(String t) {
        
    }
}

//擦除
class Comp1 implements Comparable{
    @Override
    public void compare(Object t) {
    }
}

//父类擦除,子类泛型
class Comp2<T> implements Comparable{
    @Override
    public void compare(Object t) {
    }
}

//子类泛型>=父类泛型
class Comp3<T, T1> implements Comparable<T>{
    @Override
    public void compare(T t) {
        
    }
}
View Code

 

 4.通配符         

? extends super
1.可以用在声明类型、声明方法参数上,不能用在声明类上
2.?可以接受泛型的任意类型,只能接收和输出,不能修改。(方法声明时参数无法正确知道具体的类型,因此不能修改)
3.?extends 泛型上限 <= (指定类型为子类或自身)
4. ? super 泛型下限 >= (指定类型为自身或父类)
 
package com.cy.gen03;

/**通配符
 * ? 类型不定,使用时确定类型
 * ?使用: 声明类型|声明方法上,不能声明类或使用时用?
 * 
 * 
 */
public class Student<T> {
    T score;
    
    public static void main(String[] args) {
        Student<?> stu = new Student<String>();
        test(new Student<Integer>());
        
        test2(new Student<Apple>());
        test3(new Student<Fruit>());
    }
    
    public static void test(Student<?> stu){
        
    }
    // <=
    public static void test2(Student<? extends Fruit> stu){
        
    }
    // >=
    public static void test3(Student<? super Apple> stu){
        
    }
}



class Fruit{
    
}

class Apple extends Fruit{
    
}
View Code

 

例2:?通配符的使用例子
 package com.generic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


public class GenericTest2 {

    public static void main(String[] args) {
        List<String> list1 = new ArrayList<>();
        list1.add("abc");
        print(list1);

        List<Integer> list2 = new ArrayList<>();
        list2.add(1);
        print(list2);
    }

    /**
     * ? 通配符
     * 使用? 通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,
     * 不能调用与参数化有关的方法
     */
    public static void print(Collection<?> collection) {
        /**
         * 这里不能写:collection.add("abc); 在未使用之前不知道collection中是什么类型
         * 不能调用一个与参数有关的方法。与参数类型有关的方法都不能调用。
         * 可以写:collection.size(),因为与参数无关
         */
        System.out.println("大小:" + collection.size());
        System.out.println(collection);
        for (Object o : collection) {
            System.out.println(o);
        }
    }
}

 

5.泛型类的继承与实现

package com.generic;

import java.io.Serializable;


public class GenericTest2 {

    /**
     * 泛型类的继承与实现
     * 继承Object
     * 实现了Serializable、Runnable、Comparable接口,每个接口之间用&符号连接
     */
    class TestGeneric<E extends Object & Serializable & Runnable & Comparable> {

    }
}

 

6.擦除     

java中的泛型类型(或者泛型)类似C++中的模板。但是这种相似性仅限于表面,Java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为Java厂商升级其JVM造成难以逾越的障碍。所以,java的泛型采用了可以完全在编译器中实现的擦除方法。

所以这也就解释了为什么这样的代码编译器会报错,也不构成重载:

public void add(List<String> list) {}

public void add(List<Integer> list) {}

因为在擦除之后,最后实际上生成的编译后的class文件中,add方法中实际上传的就是List,这两个方法就重复了,所以会报错。

 

7.通过反射获得泛型的实际类型参数

package com.generic;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;

/**
 * 通过反射获得泛型的实际类型参数
 */
public class GenericTest4 {

    /**
     * 很想用反射的方式来得到Vector里面装的到底是什么类型,怎么做?
     */
    public static void main(String[] args) throws NoSuchMethodException {
        //Vector<Date> v1 = new Vector<>();
        //v1.getClass()...

        Method method = GenericTest4.class.getMethod("applyVector", Vector.class);
        Type[] types = method.getGenericParameterTypes();
        ParameterizedType pType = (ParameterizedType) types[0];
        //打印原始类型
        System.out.println(pType.getRawType());
        //打印实际参数类型,可能有多个,比如Map的<K, V>有两个,这里只有1个,所以取下标0
        System.out.println(pType.getActualTypeArguments()[0]);
    }

    /**
     * 没法直接通过v1.getClass()等..来获取Vector中装的是什么类型。
     * 但是可以通过applyVector方法来知道它的参数列表的类型
     */
    public static void applyVector(Vector<Date> v1) {

    }
}

console:

class java.util.Vector
class java.util.Date

 

 

 

 

 

 

 
 
--
posted on 2018-06-21 22:45  有点懒惰的大青年  阅读(245)  评论(0)    收藏  举报