JavaSE学习笔记 Java泛型学习

Java泛型学习

  • 1.泛型的概述与基本使用
    • 1.1 ArrayList存储字符串并遍历泛型版
    • 1.2 ArrayList存储自定义对象并遍历泛型版
  • 2.泛型的由来
  • 3.泛型类的概述与使用
  • 4.泛型方法的定义与使用
  • 5.泛型接口的定义与使用
    • 5.1 具体类实现泛型接口,不明确泛型接口中类型参数变量
    • 5.2 具体实现类实现泛型接口,明确泛型接口中的参数变量
    • 5.3 子类接口继承泛型接口时,没有明确泛型接口中的参数变量
  • 6.泛型通配符
  • 总结

1.泛型的概述与基本使用

泛型概述:是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。

  • 泛型的格式为:<数据类型>,这里的数据类型只能是引用数据类型

相关术语:

  • ArrayList< 数据类型 > 中数据类型称为类型参数变量
  • ArrayList < String > 中的String称为实际类型参数
  • 将ArrayList< 数据类型 >整体称为泛型类型
  • 将ArrayList< String >整体称为参数化的类型

1.1 ArrayList存储字符串并遍历泛型版

  • 利用ArrayList存储字符串并遍历泛型版
import java.util.ArrayList;

public class MyTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("bcd");
        list.add("edf");

        for (int i = 0; i < list.size(); i++) {
            //使用泛型,避免了向下转型
            String s = list.get(i);
            System.out.println(s);

        }
    }
}

运行后的结果为:
JavaSE学习笔记 Java泛型学习_第1张图片


1.2 ArrayList存储自定义对象并遍历泛型版

  • ArrayList存储自定义对象并遍历泛型版
//自定义类:Student类
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

//测试类
import java.util.ArrayList;
import java.util.function.Consumer;

public class MyTest2 {
    public static void main(String[] args) {
        ArrayList<Student> studentlist = new ArrayList<>();

        studentlist.add(new Student("张三",23));
        studentlist.add(new Student("李四",24));
        studentlist.add(new Student("王五",25));


        studentlist.forEach(new Consumer<Student>() {
            @Override
            //避免向下转型
            public void accept(Student student) {
                System.out.println(student);
            }
        });

        System.out.println("============");
        for (int i = 0; i < studentlist.size(); i++) {
            //避免了向下转型
            Student student = studentlist.get(i);
            System.out.println(student);

        }
    }
}

运行后的结果为:
JavaSE学习笔记 Java泛型学习_第2张图片


2.泛型的由来

在JDK1.5之前没有泛型这个概念,JDK1.5之后添加类泛型。泛型的由来:是由Object转型问题引入。早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。当有类型转换就会存在安全隐患,所以Java就提供泛型来解决安全问题。

  • 泛型最常用的是在集合上。我们了解到Collection、Map集合对存储元素的类型没有任何限制的。把元素存储在集合中后,集合并不知道元素的类型是什么类型,仅仅知道是Object。因此在获得元素时,返回的是Object类型。接着还要进行类型转换。
public class MyList {
    //将Object对象设置为成员变量
    private Object obj;

    //提供set,get方法
    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }
}


import java.util.List;
public class MyTest {
    public static void main(String[] args) {
        /*因为集合中能存储任何引用数据类型的元素,JDK1.5之前可以使用Object类型来存储
      但在获取时返回的是Object类型,在具体使用时需要向下转型,这样就存在一定的安全
      问题*/
        MyList list= new MyList();
        list.setObj("abc");
        MyList list2 = new MyList();
        list2.setObj(100);

        Object obj = list.getObj();
        //将Object数据类型转换成Strings数据类型
        String str= (String) obj;
        int length = str.length();
        System.out.println(length);

        Object obj1 = list2.getObj();
        //将Object数据类型转换成Integer数据类型
        Integer num= (Integer) obj1;
        System.out.println(num);

    }
}


JDK1.5之后有了泛型,泛型的好处在于:

1.把运行时期的问题提前到编译期间,提高了代码的健硕性,只要编译期没有警告,那么运行期就不会出现ClassCastException(类强制转换异常)

2.避免了强制类型转换

3.优化成程序设计,解决了黄色警告线

注意:泛型只在编译期有效,在运行期就进行擦除了


3.泛型类的概述与使用

泛型类就是把泛型定义在类上,用户在使用该类时,才把类型确定下来。

泛型类的定义格式:public class 类名< 泛型类型1,… >

定义泛型类后,用户在使用该类时,需要将类型确定下来,这样就不用担心类型强制异常的问题。

//T :泛型表示某种引用数据类型
//泛型类:就是将泛型定义在类上
public class Demo<T> {
    private T t;

    public T getT() {
        return t;
    }

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

public class MyTest {
    public static void main(String[] args) {
        //泛型机制:就是将数据类型明确工作推迟到创建对象时或者调用方法时才去明确的一种机制。
        Demo<String> stringDemo = new Demo<>();
        stringDemo.setT("abc");
        //避免了向下转型
        String t = stringDemo.getT();
        System.out.println(t);

        System.out.println("======");
        Demo<Integer> integerDemo = new Demo<>();
        integerDemo.setT(100);
        Integer t1 = integerDemo.getT();
        System.out.println(t1);

    }
}
//定义多个泛型类型变量,用逗号进行隔开
public class Demo2<R,T> {
    private R r;
    private T t;

    public R getR() {
        return r;
    }

    public void setR(R r) {
        this.r = r;
    }

    public T getT() {
        return t;
    }

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

public class MyTest2 {
    public static void main(String[] args) {
        Demo2<String, Integer> stringIntegerDemo2 = new Demo2<>();
        stringIntegerDemo2.setR("abc");
        stringIntegerDemo2.setT(100);
        //避免向下转型
        String r = stringIntegerDemo2.getR();
        System.out.println(r);
        //避免向下转型
        Integer t = stringIntegerDemo2.getT();
        System.out.println(t);

    }
}


4.泛型方法的定义与使用

泛型方法就是把泛型定义在方法上,用户传递进来的是什么类型,回值就是什么类型。

public class MyUtils {
    //通常我们定义一个方法,会将参数的类型进行确定。
    public void show(String string){
        System.out.println(string);
    }
    /*但我们想要通过一个方法,可以传入多种参数进行返回,我们首先
      想到的是传入的类型为Object类型,但是使用Object类型有一个弊端:
      当想要具体使用传入的参数时,需要向下转型
    public void show(Object obj){
        System.out.println(obj);
    }

     */

    /*其实在Java中我们也可以通过泛型方法,传入多种参数进行返回,
      并且也会避免向下转型,防止发生类型转换异常*/
    public<T> void show(T t){
        System.out.println(t);
    }
}

//测试类
public class MyTest {
    public static void main(String[] args) {
        MyUtils myUtils = new MyUtils();
        myUtils.show("abc");
        System.out.println("==========");
        MyUtils myUtils1 = new MyUtils();
        //调用方法,传入的类型是什么,返回值的类型就是什么
        myUtils1.show(123);
        myUtils1.show("123");
        myUtils.show(123.4);
        myUtils1.show(true);
    }
}


5.泛型接口的定义与使用

泛型可以定义在接口上,称为泛型接口

定义的格式:public interface MyInterface< T >

//泛型可以定义在接口上,称为泛型接口
public interface MyInterface <T,R>{
    public abstract R add(T t);
}

public class MyTest {
    public static void main(String[] args) {

        //接口上定义泛型的具体数据类型,到底在什么时候进行明确呢

        //创建接口的子类对象时,需要明确接口上泛型的具体数据类型
        new MyInterface<String, Integer>() {
            @Override
            public Integer add(String s) {
                return null;
            }
        };
    }
}

我们已经知道了泛型接口的定义,泛型接口就是将泛型定义在接口上。接口可以被具体的类进行实现。

那它是怎样进行实现的?这里可以分两种情况:

  • 1.具体类实现泛型接口不明确泛型接口中的类型参数变量
  • 2.具体类实现泛型接口明确泛型接口中的类型参数变量

5.1 具体类实现泛型接口,不明确泛型接口中类型参数变量

接口中实现类在实现接口时,可以先不明确泛型接口的参数类型,等到创建接口实现类的对象时明确参数类型即可。

//在接口上定义泛型
public interface MyInterface<T,R> {
    public abstract R add(T t);

}


/*接口实现类在实现接口时,可以先不明确参数类型,
        等到创建接口实现类的对象时明确参数类型即可*/
public class MyUtils<T,R> implements MyInterface<T,R> {
    @Override
    public R add(T t) {
        return null;
    }
}

public class MyTest {
    public static void main(String[] args) {

        MyUtils<String, String> stringStringMyUtils = new MyUtils<>();
        String abc = stringStringMyUtils.add("abc");
        System.out.println(abc);
    }
}

5.2 具体实现类实现泛型接口,明确泛型接口中的参数变量

接口中实现类在实现接口时,直接明确泛型接口中的参数变量。

//在接口上定义泛型
public interface MyInterface<T,R> {
    public abstract R add(T t);
}


//接口的具体实现类在实现泛型接口时,可以直接明确接口中的泛型
public class MyUtils implements MyInterface<String,String> {
    @Override
    public String add(String s) {
        return null;
    }
}

public class MyTest {
    public static void main(String[] args) {
        MyUtils myUtils = new MyUtils();
        String abc = myUtils.add("abc");
        System.out.println(abc);
    }
}

5.3 子类接口继承泛型接口时,没有明确泛型接口中的参数变量

//定义泛型接口
public interface MyInterface<T,R> {
    public  abstract R add(T t);
} 

//子类接口继承泛型接口时,没有明确泛型接口中的参数类型,直到创建子类接口的具体对象时明确参数类型
public interface MyInterface2<T,R> extends MyInterface<T,R> {
    @Override
    R add(T t);
}

public class MyTest {
    public static void main(String[] args) {
        String abc = new MyInterface2<String, String>() {
            @Override
            public String add(String s) {
                return null;
            }
        }.add("abc");


        System.out.println(abc);

    }
}



6.泛型通配符

泛型通配符 含义
泛型通配符 任意类型,如果没有明确,那就是Object类以及任意的Java类
? extends E 向下限定,E以及其子类
? super E 向上限定,E以及其父类
public class MyTest {
    public static void main(String[] args) {
        //泛型通配符的使用
        //? 表示任意的数据类型
        Animal<?> objectAnimal = new Animal<>();

        //? super E:向上限定,?表示的是E或者是E的父类
        Animal<? super Animal> animal1 = new Animal<Animal>();
        Animal<? super Animal> animal2 = new Animal<Object>();
        //报错
       // Animal animal3 = new Animal();
       // Animal aniaml4 = new Animal();


      //? extends E:向下限定,?表示的是E或者是E的子类
        Animal<? extends Animal> animal5 = new Animal<Animal>();
        Animal<? extends Animal> animal6 = new Animal<Dog>();
        Animal<? extends Animal> animal7 = new Animal<Cat>();
        //报错
        //Animal animal8 = new Animal();


    }
}

//Animal泛型类
class  Animal<T>{

}
//Dog泛型类
class  Dog<T> extends Animal<T>{

}

//Cat泛型类
class Cat<T> extends Animal<T>{

}



总结

本节主要学习了Java泛型,了解泛型的由来。其次学习了泛型类,泛型接口以及泛型方法的定义与使用,最后对于泛型通配符有了一定的认识。
JavaSE学习笔记 Java泛型学习_第3张图片

你可能感兴趣的:(JAVASE,泛型,java,编程语言,泛型)