转:Java泛型

泛型

泛型就是参数化类型

  • 适用于多种数据类型执行相同的代码
  • 泛型中的类型在使用时指定
  • 泛型归根到底就是“模版”

优点:使用泛型时,在实际使用之前类型就已经确定了,不需要强制类型转换。

泛型主要使用在集合中

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


public class Demo01 {
    // 不使用泛型,存取数据麻烦
    public static void test1() {
        List list = new ArrayList();
        list.add(100);
        list.add("zhang");

        /*
         * 从集合中获取的数据是Object类型,Object类型是所有类型的根类,但是在具体使用的时候需要
         * 类型检查,类型转化,处理类型转化异常
         * 使用麻烦
         */
        Object o = list.get(1);

        if (o instanceof String) {
            String s = (String) o;
        }

        System.out.println(o);
    }

    // 使用泛型
    public static void test2() {
        List<String> list = new ArrayList<String>();
        //list.add(100); 放数据时安全检查,100不是String类型,不能存放
        list.add("存数据安全,取数据省心");

        String s = list.get(0); //取出来的数据直接就是泛型规定的类型
        System.out.println(s);
    }

    public static void main(String[] args) {
        test1();
        test2();
    }
}

自定义泛型

泛型字母

  • 形式类型参数(formal type parameters)即泛型字母
  • 命名泛型字母可以随意指定,尽量使用单个的大写字母(有时候多个泛型类型时会加上数字,比如T1,T2)
    常见字母(见名知意)

    • T Type
    • K V Key Value
    • E Element
  • 当类被使用时,会使用具体的实际类型参数(actual type argument)代替

泛型类

  • 只能用在成员变量上,只能使用引用类型

泛型接口

  • 只能用在抽象方法上

泛型方法

  • 返回值前面加上<T>

泛型类 

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map

/**
 * 自定义泛型类
 *
 * 定义"模版"的时候,泛型用泛型字母:T 代替
 * 在使用的时候指定实际类型
 *
 * @author Administrator
 * @param <T>
 */
public class Student<T> {
    private T javase;

    //private static T javaee;   // 泛型不能使用在静态属性上
    public Student() {
    }

    public Student(T javase) {
        this();
        this.javase = javase;
    }

    public T getJavase() {
        return javase;
    }

    public void setJavase(T javase) {
        this.javase = javase;
    }
}


/**
 * 自定义泛型的使用
 * 在声明时指定具体的类型
 * 不能为基本类型
 * @author Administrator
 *
 */
class Demo02 {
    public static void main(String[] args) {
        //Student<int>  Student = new Student<int>(); //不能为基本类型,编译时异常
        Student<Integer> student = new Student<Integer>();
        student.setJavase(85);
        System.out.println(student.getJavase());
    }
}

注意:

  • 泛型的类型参数只能是类类型,不能是简单类型
  • 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
if(ex_num instanceof Generic<Number>){   
}

泛型接口 

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。

/**
 * 自定义泛型接口
 *
 * 接口中泛型字母只能使用在方法中,不能使用在全局常量中
 *
 * @author Administrator
 * @param <T>
 */
public interface Comparator<T1, T2> {
    //public static final T1 MAX_VALUE = 100; //接口中泛型字母不能使用在全局常量中
    //T1 MAX_VALUE;
    public static final int MAX_VALUE = 100;

    void compare(T2 t);

    T2 compare();

    public abstract T1 compare2(T2 t);
}

当实现泛型接口的类,未传入泛型实参时:

  • 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中,

即:class FruitGenerator<T> implements Generator<T>

  •  如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
/**
 * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
 * 即:class FruitGenerator<T> implements Generator<T>{
 * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

当实现泛型接口的类,传入泛型实参时:

/**
 * 传入泛型实参时:
 * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
 * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
 * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
 */
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)];
    }
}

 泛型方法

非泛型类中泛型方法的使用:

import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import java.util.List;


/**
 * 非泛型类中定义泛型方法
 * @author Administrator
 *
 */
public class Method {
    // 泛型方法,在返回类型前面使用泛型字母
    public static <T> void test1(T t) {
        System.out.println(t);
    }

    // T 只能是list 或者list 的子类
    public static <T extends List> void test2(T t) {
        t.add("aa");
    }

    // T... 可变参数   --->   T[]
    public static <T extends Closeable> void test3(T...a) {
        for (T temp : a) {
            try {
                if (null != temp) {
                    temp.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws FileNotFoundException {
        test1("java 是门好语言");
        test3(new FileInputStream("a.txt"));
    }
}

泛型类中泛型方法的使用 :

public class GenericFruit {
    class Fruit{
        @Override
        public String toString() {
            return "fruit";
        }
    }

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

    class Person{
        @Override
        public String toString() {
            return "Person";
        }
    }

    class GenerateTest<T>{
        public void show_1(T t){
            System.out.println(t.toString());
        }

        //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
        //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        Person person = new Person();

        GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
        //apple是Fruit的子类,所以这里可以
        generateTest.show_1(apple);
        //编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
        //generateTest.show_1(person);

        //使用这两个方法都可以成功
        generateTest.show_2(apple);
        generateTest.show_2(person);

        //使用这两个方法也都可以成功
        generateTest.show_3(apple);
        generateTest.show_3(person);
    }
}

泛型的继承

/**
 * 泛型继承
 *
 * 保留父类泛型 ----》泛型子类
 * 不保留父类泛型 -----》子类按需实现
 *
 * 子类重写父类的方法,泛型类型随父类而定 子类使用父类的属性,该属性类型随父类定义的泛型
 *
 * @author Administrator
 *
 * @param <T1>
 * @param <T2>
 */
public abstract class Father<T1, T2> {
    T1 age;

    public abstract void test(T2 name);
}


// 保留父类泛型 ----》泛型子类
// 1)全部保留
class C1<T1, T2> extends Father<T1, T2> {
    @Override
    public void test(T2 name) {
    }
}


// 2) 部分保留
class C2<T1> extends Father<T1, Integer> {
    @Override
    public void test(Integer name) {
    }
}


// 不保留父类泛型 -----》子类按需实现
// 1)具体类型
class C3 extends Father<String, Integer> {
    @Override
    public void test(Integer name) {
    }
}


// 2)没有具体类型
// 泛型擦除:实现或继承父类的子类,没有指定类型,类似于Object
class C4 extends Father {
    @Override
    public void test(Object name) {
    }
}
/**
 * 泛型擦除
 * 类似于Object,不等于Object
 * @author Administrator
 *
 */
public class Demo03 {
    public static void test(Student<Integer> student) {
        student.setJavase(100);
    }

    public static void main(String[] args) {
        // 泛型擦除
        Student student = new Student();
        test(student);

        Student<Object> student2 = new Student<Object>();

        //test(student2);  //编译异常
    }
}

泛型通配符

通配符(Wildcards)

  • T、K、V、E 等泛型字母为有类型,类型参数赋予具体的值
  • ?未知类型 类型参数赋予不确定值,任意类型
  • 只能用在声明类型、方法参数上,不能用在定义泛型类上

基本使用样例

/**
 * 泛型的通配符 类型不确定,用于声明变量或者形参上面
 *
 * 不能使用在类上 或者  new 创建对象上
 * @author Administrator
 *
 */
public class Demo04 {
    // 用在形参上
    public static void test(List<?> list) {
        List<?> list2; // 用在声明变量上
        list2 = new ArrayList<String>();
        list2 = new ArrayList<Integer>();
        list2 = new ArrayList<Object>();
    }

    public static void main(String[] args) {
        test(new ArrayList<String>());
        test(new ArrayList<Integer>());
    }
}

注意:

我们知道IngeterNumber的一个子类,并且知道Generic<Ingeter>Generic<Number>实际上是相同的一种基本类型。但是Generic<Ingeter>不能看做是Generic<Number>的子类。

如下面的例子中:

public void showKeyValue1(Generic<Number> obj){
    Log.d("泛型测试","key value is " + obj.getKey());
}

Generic<Integer> gInteger = new Generic<Integer>(123);
Generic<Number> gNumber = new Generic<Number>(456);

showKeyValue(gNumber);

// showKeyValue这个方法编译器会为我们报错:Generic<java.lang.Integer> 
// cannot be applied to Generic<java.lang.Number>
// showKeyValue(gInteger);

由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的

回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic<Integer>类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic<Integer>和Generic<Number>父类的引用类型。

类型通配符可以解决这个问题:

public void showKeyValue1(Generic<?> obj){
    Log.d("泛型测试","key value is " + obj.getKey());
}

类型通配符可以解决当具体类型不确定的时候,这个通配符就是 ?  ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。 

泛型上下边界

在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类

上边界(extends)

传入的类型实参必须是指定类型的子类型

  • ? extends List

下边界(super)

即父类或本身

  • ? super List
/**
 * extends:泛型的上限 <= 一般用于限制操作 不能使用在添加数据上,一般都是用于数据的读取
 *
 * supper:泛型的上限 >= 即父类或自身。一般用于下限操作
 *
 * @author Administrator
 * @param <T>
 */
public class Test<T extends Fruit> {
    private static void test01() {
        Test<Fruit> t1 = new Test<Fruit>();
        Test<Apple> t2 = new Test<Apple>();
        Test<Pear> t3 = new Test<Pear>();
    }

    private static void test02(List<?extends Fruit> list) {
    }

    private static void test03(List<?super Apple> list) {
    }

    public static void main(String[] args) {
        // 调用test02(),测试 extends  <=
        test02(new ArrayList<Fruit>());
        test02(new ArrayList<Apple>());
        test02(new ArrayList<ReadApple>());
        // test02(new ArrayList<Object>()); Object 不是 Fruit 的子类 ,编译不通过

        // 调用test03() ,测试super >=
        test03(new ArrayList<Apple>());
        test03(new ArrayList<Fruit>());

        //test03(new ArrayList<ReadApple>());  ReadApple < apple,所以不能放入
    }
}


class Fruit {
}


class Apple extends Fruit {
}


class Pear extends Fruit {
}


class ReadApple extends Apple {
}

泛型嵌套

从外向里取

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


/**
 * 泛型嵌套
 * @author Administrator
 *
 */
public class Demo05 {
    public static void main(String[] args) {
        Student2<String> student = new Student2<String>();
        student.setScore("优秀");
        System.out.println(student.getScore());

        //泛型嵌套
        School<Student2<String>> school = new School<Student2<String>>();
        school.setStu(student);

        String s = school.getStu().getScore(); //从外向里取
        System.out.println(s);

        // hashmap 使用了泛型的嵌套
        Map<String, String> map = new HashMap<String, String>();
        map.put("a", "张三");
        map.put("b", "李四");

        Set<Entry<String, String>> set = map.entrySet();

        for (Entry<String, String> entry : set) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
}
public class School<T> {
    private T stu;

    public T getStu() {
        return stu;
    }

    public void setStu(T stu) {
        this.stu = stu;
    }
}
public class Student2<T> {
    T score;

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }
}

其他

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


/**
 * 泛型没有多态
 * 泛型没有数组
 * JDK1.7对泛型的简化
 * @author Administrator
 *
 */
public class Demo06 {
    public static void main(String[] args) {
        Fruit fruit = new Apple(); // 多态,父类的引用指向子类的对象
                                   //List<Fruit> list = new ArrayList<Apple>(); //泛型没有多态 

        List<?extends Fruit> list = new ArrayList<Apple>();

        //泛型没有数组
        //Fruit<String>[] fruits = new Fruit<String>[10];

        //ArrayList底层是一个Object[],它放数据的时候直接放,取数据的时候强制类型转化为泛型类型
        /*public boolean add(E e) {
              ensureCapacityInternal(size + 1);  // Increments modCount!!
              elementData[size++] = e;
              return true;
          }*/

        /*E elementData(int index) {
              return (E) elementData[index];
          }*/

        //JDK1.7泛型的简化,1.6编译通不过
        List<Fruit> list2 = new ArrayList<>();
    }
}

 

博客

Java知识点总结(Java泛型)

java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值