day15_泛型

泛型

学习目标

1.理解泛型的作用
2.熟练自定义泛型
3.掌握泛型的高级使用
4.泛型应用

一、泛型介绍

从JDK5以后,Java引入了“参数化类型(Parameterized type)”的概念。

所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时确定**(例如,继承或实现这个接口,用这个类型声明变量、创建对象时即传入实 际的类型参数,也称为类型实参)**

泛型入门案例:

/**
  泛型入门案例:
 */
// 用T表示一种数据类型
public class Person<T> {
    private int id;
    private String name;
    //定义T类型属性,T类型在使用时确定
    private T t;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public T getT() {
        return t;
    }

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

    public static void main(String[] args) {
        //创建对象,指定泛型T的类型
        Person<Integer> person = new Person<Integer>();
        //设置属性
        person.setId(1);
        person.setName("小红");
        person.setT(12);
        //取值
        System.out.println(person.getId());
        System.out.println(person.getName());
        System.out.println(person.getT());

        //创建对象,指定泛型T的类型
        Person<String> person2 = new Person<String>();
        //设置属性
        person2.setId(2);
        person2.setName("小李");
        person2.setT("abc");
        //取值
        System.out.println(person2.getId());
        System.out.println(person2.getName());
        System.out.println(person2.getT());
    }
}

二、自定义泛型结构

2.1 自定义泛型类

自定义泛型类语法

在这里插入图片描述

示例1:自定义泛型类

/**
	1.给类定义泛型 
 */
// 1.定义泛型
public class User<T,E> {
	private int id;
	private String name;
	private String pwd;
	//2.属性: 使用泛型
	private T t;
	private E e;
	//3.方法: 使用泛型
	public T getT() {
		return t;
	}
	public void setT(T t) {
		this.t = t;
	}
	public E getE() {
		return e;
	}
	public void setE(E e) {
		this.e = e;
	}
	
	//4.构造器:使用泛型
	public User(int id, String name, String pwd, T t, E e) {
		this.id = id;
		this.name = name;
		this.pwd = pwd;
		this.t = t;
		this.e = e;
	}
	
	public User() {

	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + ", t=" + t + ", e=" + e + "]";
	}
	public static void main(String[] args) {
		//1.使用泛型并测试
		User<String,Integer> user = new User<String, Integer>(1, "小黑", "123456", "泛型1", 22);
		//2.调用属性
		Integer i = user.e;
		String str = user.t;
		System.out.println(i);
		System.out.println(str);
		//3.方法
		user.setE(1);
		user.setT("字符串类型");
		Integer i2 = user.getE();
		String str2 = user.getT();
		System.out.println(user);
	}
	
}

2.2 自定义泛型方法

方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

泛型方法语法

在这里插入图片描述

示例1:泛型方法

public class MethodType {
	public static <T> void method1(T t) {
		System.out.println(t);
	}
	public static <T> T method2(T t) {
		return t;
	}
	public static void main(String[] args) {
		MethodType.method1("小黑");
		Date date = MethodType.method2(new Date());
		System.out.println(date);
	}
}

2.3 泛型使用注意事项

  • 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
  • jdk7,泛型的简化操作:ArrayList flist = new ArrayList<>();
  • 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
  • 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。
  • 异常类不能是泛型的
  • 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
    • 子类不保留父类的泛型:按需实现
      • 没有类型 擦除
      • 具体类型
    • 子类保留父类的泛型:泛型子类
      • 全部保留
      • 部分保留

示例1:泛型使用注意事项

/**
 * 泛型的使用注意事项
 *   1.泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
 *   2.jdk7,泛型的简化操作: User<String> user = new User<>();
 *   3.泛型的指定中不能使用基本数据类型,可以使用包装类替换。
 *   4.在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。
 *   5.异常类不能是泛型的
 */
public class Demo1 {
    public static void main(String[] args) {
        //泛型不指定,将被擦除,按照Object类型处理
        User user = new User();
        user.setT("abc");
        System.out.println(user.getT());
        //指定泛型的简化写法
        User<String> user2 = new User<>();
        user2.setT("abc");
        //泛型只能指定为引用类型,不能是基本类型
        //User<int> user3 = new User<>(); 报错
    }
}
class User<T>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
    //在类上声明的泛型不可以在静态方法中使用
//    public static T method1(T t){
//        return t;
//    }
}

示例2:泛型的注意事项

/**
 * 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
 * 1.子类不保留父类的泛型:按需实现
 *      1.没有类型 擦除
 *      2.具体类型
 *  2.子类保留父类的泛型:泛型子类
 *      1.全部保留
 *      2.部分保留
 */
public class Demo2 {
    public static void main(String[] args) {
    }
}
class Father<A,B>{
    public A a;
    public B b;
}
class Son1 extends Father{}//子类不保留父类泛型,等价于class Son1 extends Father<Object,Object>
class Son2 extends Father<String,Integer>{}//子类不保留父类泛型,但给父类指定泛型的类型
class Son3<A,B> extends Father<A,B>{}//子类全部保留父类泛型
class Son4<A> extends Father<A,String>{}//子类保留父类部分泛型

2.4 小结

  1. 泛型可以声明在哪些位置(类、接口、方法)
  2. 使用泛型的注意事项

三、泛型的高级应用

3.1 指定泛型上限

在声明泛型的时候,可以通过extends关键字指定泛型的上限。

示例1:泛型只能是B类型或者B类型的子类

/**
 * 指定泛型的上限
 */
public class ParameterizedTypeDemo<T extends B> {
    private T t;

    public T getT() {
        return t;
    }

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

    public static void main(String[] args) {
        //泛型只能使用为B类型或者B类型的子类
        ParameterizedTypeDemo<C> parameterizedTypeDemo = new ParameterizedTypeDemo<>();
        //报错
        //ParameterizedTypeDemo<String> stringParameterizedTypeDemo = new ParameterizedTypeDemo<String>();
        //报错
        //ParameterizedTypeDemo<A> aParameterizedTypeDemo2 = new ParameterizedTypeDemo<A>();
    }
}
class A {}
class B  extends A{}
class C extends B{}

3.2 在继承方面的注意事项

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的 类或接口,G<B>并不是G<A>的子类型!

比如:String是Object的子类,但是User<String >并不是User<Object> 的子类; 这其实是很显而易见的问题,两者都是User类型,只是泛型不同,这是一道大厂面试题。

3.3 通配符的使用

虽然说但是User<String >并不是User<Object> 的子类,但是User确实存在父类的;这就涉及到了?通配符的使用,通配符语法如下:

  1. ? 代表通配符,User<?> 是User、User、User的父类
  2. 读取User<?>的对象的属性,永远是安全的,因为不管User的真实类型是什么,它包含的都是Object。
  3. 写入User<?>的对象属性不行。因为我们不知道?的元素类型,我们不能设置属性值;唯一的例外是null,它是所有类型的成员

示例1:通配符的使用

package top.psjj.demo1;

/**
 * 泛型通配符的使用
 *  1. ? 代表通配符,User<?> 是User<String>、User<Integer>、User<Object>的父类
	2. 读取User<?>的对象的属性,永远是安全的,因为不管User的真实类型是什么,它包含的都是Object。
	3. 写入User<?>的对象属性不行。因为我们不知道?的元素类型,我们不能设置属性值;唯一的例外是null,它 是所有类型的成员
 */
public class ParameterizedTypeDemo2{
    public static void main(String[] args) {
        User<String> user = new User<>();
        //user2是user的父类
        User<?> user2 = new User<>();
        //读取User<?>的对象的属性,永远是安全的,因为不管user的真实类型是什么,它包含的都是Object。
        System.out.println(user2.getT());
	   //3. 写入User<?>的对象属性不行。因为我们不知道?的元素类型,我们不能设置属性值;唯一的例外是null,它 是所有类型的成员
       // user2.setT(10); //报错
        user2.setT(null);
    }
}
class User<T>{
    private T t;

    public T getT() {
        return t;
    }

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

3.4 通配符的上限与下限

通配符的上限与下限指定

  • 上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
  • 下限super:使用时指定的类型不能小于操作的类,即>=

示例1:指定通配符上限与下限

/**
 * 通配符的上限与下限指定
 *  1.上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
 *  2.下限super:使用时指定的类型不能小于操作的类,即>=
 */
public class Student<T>{
    private T t;

    public T getT() {
        return t;
    }

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

    public static void main(String[] args) {
        //指定通配符的上限泛型Number
        Student<? extends Number> student = new Student();
        //泛型为Integer类型的student1 是上面类的子类
        Student<Integer> student1 = new Student<>();
        //泛型为String类型的student2 和 student没有关系
        Student<String> student2 = new Student<>();
        //向上转型成立
        student = student1;
        //向上转型失败
        //student = student2;

        //指定通配符的下限泛型Integer
        Student<? super Integer> s1 = new Student();
        Student<Number> s2 = new Student<>();
    }
}

注意: 通配符**?**是泛型的使用,而不是声明,所以不能在类声明,方法声明上使用

在这里插入图片描述

通配符只能使用在对象创建的左侧

在这里插入图片描述

3.5 小结

  1. 指定泛型的上限
  2. 通配符的使用

四、泛型应用

无论是自然排序还是定制排序接口底层都是声明泛型,如下所示:

在这里插入图片描述

4.1 自然排序应用泛型

示例1:按照用户年龄排序

/*
自然排序
*/
public class User implements Comparable<User> {
    private int id;
    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

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

    public User() {
    }

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

    @Override
    public int compareTo(User u) {
        if(u==null){
            throw new NullPointerException("null不能参与排序");
        }
        return this.age-u.getAge();
    }

    public static void main(String[] args) {
        User[] users = new User[3];
        for (int i = 0; i < users.length ; i++) {
            users[i] = new User((i+1),"张三"+i,(int)(Math.random()*100));
        }
        //数组排序
        Arrays.sort(users);
        //查看数组内容
        System.out.println(Arrays.toString(users));
    }
}

4.2 定制排序应用泛型

示例2:按照用户年龄排序

public class User {
    private int id;
    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

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

    public User() {
    }

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

    public static void main(String[] args) {
        User[] users = new User[3];
        for (int i = 0; i < users.length ; i++) {
            users[i] = new User((i+1),"张三"+i,(int)(Math.random()*100));
        }
        //数组排序:定制排序
        Arrays.sort(users,new Comparator<User>(){
            @Override
            public int compare(User u1, User u2) {
                if(u1==null || u2==null){
                    throw new NullPointerException("排序的用户不能为null");
                }
                return u1.getAge()-u2.getAge();
            }
        });
        //查看数组内容
        System.out.println(Arrays.toString(users));
    }
}

五、总结

  1. 泛型的声明与使用
  2. 泛型的上限
  3. 泛型的通配符
  4. 泛型应用
  5. 泛型可以避免类型转换异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值