Java自学之泛型

在Java语言中,为了方便接收参数类型的统一,提供了核心类Object,利用此类对象可以接收所有类型的数据(包括基本数据类型和引用数据类型)。但是由于其所描述的数据范围过大,所以在实际使用中就会出现传入数据类型错误,从而引发异常(主要是强制向下转型引发)。如果想要解决项目中可能出现的这种异常情况,最为核心的方案就是避免强制性地进行对象向下转型操作。所以泛型设计的核心思想在于:类中的属性或方法的参数与返回值的类型采用动态标志,在对象实例化的时候动态配置要使用的数据类型。

class Point<T> {
	private T x;
	private T y;
	public void setX(T x) {
		this.x = x;
	}
	public void setY(T y) {
		this.y = y;
	}
	public T getX() {
		return this.x;
	}
	public T getY() {
		return this.y;
	}
}
class JavaDemo {
	public static void main(String[] args) {
        // 设置int类型
		Point<Integer> p = new Point<Integer>();
		p.setX(120);
		p.setT(100);
		int x = p.getX();
		int y = p.getY();
		System.out.println("x坐标:" + x + ",y坐标:" + y);
        // 设置String类型
        Point<String> p2 = new Point<String>();
        p2.setX('x坐标:100');
        p2.setY('y坐标:120');
        String m = p2.getX();
        String n = p2.getY();
        System.out.println(m + ',' + n);
	}
}

泛型在类上标志出现后,需要通过实例化对象进行类型的设置,而所设置的类型只能是引用数据类型。如果要设置基本数据类型,则必须采用包装类的形式。

泛型通配符

利用泛型类在实例化对象时进行的动态类型匹配,虽然可以有效地解决对象向下转型的安全隐患,但是在程序中实例化泛型类对象时,不同泛型类型的对象之间彼此是无法进行引用传递的,所以在进行泛型类型的引用对象时,为了可以适应所有本类的实例化对象,则可以在接受时使用【“?”】作为泛型通配符使用,利用【“?”】表示的泛型类型只允许从对象中获取数据,而不允许修改数据。

class Message<T> {
	private T content;
	public void setContent(T content) {
		this.content = content;
	}
	public T getContent() {
		return this.content;
	}
}
class JavaDemo {
	public static void main(String[] args) {
		Message<String> msg = new Message<String>();
		msg.setContent('www.uihtml.cn');
		printMsg(msg);
	}
	public static void printMsg(Message<?> msg) {
		System.out.println(msg.getContent());
	}
}

本程序在printMsg()方法的参数上使用Message<?>接收Message类的引用对象,由于通配符【“?”】的作用,所以该方法可以匹配任意的泛型类型(Message<String>或Message<Integer>等都可以)。

通配符【“?”】除了可以匹配任意的泛型类型外,也可以通过泛型上限和下限的配置实现更加严格的类范围定义。

  • 【类和方法】设置泛型的上限【? extends 类】:只能使用当前类或当前类的子类设置泛型类型。
  • 【方法】设置泛型的下限【? super 类】:只能设置指定的类或指定类的父类。

设置泛型上限

class Message<T extends Number> {
	private T content;
	public void setContent(T content) {
		this.content = content;
	}
	public T getContent() {
		return this.content;
	}
}

可以设置Number类或Number的子类(Integer、Double)

class JavaDemo {
	public static void main(String[] args) {
		Message<Integer> msg = new Message<Integer>();
		msg.setContent(100);
		printMsg(msg);
        Message<Double> msg1 = new Message<Double>();
        msg1.setContent(99.5);
        printMsg(msg1);
	} 
	public static printMsg(Message<? extends Number> msg) {
		System.out.println('期末考试成绩为:' + msg.getContent());
	}
}

设置泛型下限

class Message<T> {
	private T content;
	public void setContent(T content) {
		this.content = content;
	}
	public T getContent() {
		return this.content;
	}
} 
class JavaDemo {
	public static void main(String[] args) {
		Message<String> msg = new Message<String>();
        msg.setContent('最好的程序员学习平台:www.uihtml.cn');
        printMsg(msg);
	}
	public static void PrintMsg(Message<? super String> msg) {
		System.out.println(msg.getContent());
	}
}

泛型接口

定义泛型接口子类,在子类中继续声明泛型

interface IMessage<T> {
	public String echo(T msg);
}
class MessageImpl implements IMessage<S> {
	@Override
	public String echo(S t) {
		return '【ECHO】' + t;
	}
}
class JavaDemo {
	public static void main(String[] args) {
		IMessage<String> msg = new MessageImpl<String>();
		System.out.println(msg.echo('www.uihtml.cn'));
	}
}

本程序定义MessageImpl子类时继续声明了一个泛型标记S,并且实例化MessageImpl子类对象时设置的泛型类型也会传递到IMessage接口中。

定义子类,在子类中设置泛型类型

interface IMessage<T> {
	public String echo(T msg);
}
class MessageImpl implements IMessage<String> {
	public String echo(String t) {
		return '【ECHO】' + t;
	}
}
class JavaDemo {
	public static void main(String[] args) {
		IMessage<String> msg = new MessageImpl();
		System.out.println(msg.echo('www.uihtml.cn'));
	}
}

本程序在定义MessageImpl子类时没有定义泛型标记,而是为父接口设置泛型类型为String,所以在覆写echo()方法时参数的类型就是String。

泛型方法

对于泛型,除了可以定义在类上之外,也可以在方法上进行定义,而在方法上定义泛型的时候,这个方法不一定非要在泛型类型定义。

class JavaDemo {
	public static void main(String[] args) {
		Integer num[] = fun(1,2,3,4,5);
		for (int temp : num) {
			System.out.println(temp + '、');
		}
	}
	public static <T> T[] fun(T... args) {
		return args;
	}
}

由于此时是在一个没有泛型声明的类中定义了泛型方法,所以在fun()方法声明处就必须单独定义泛型标记,此时的泛型类型将由传入的参数类型来决定。

原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/java/java-genericity.html

Like (0)
Donate 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ZEROZERO
Previous 2020年12月7日
Next 2020年12月10日

相关推荐

  • Java自学之抽象类与接口

    面向对象程序设计中,类继承的主要作用的扩充已有类的功能。子类可以根据自己的需要选择是否要覆写父类中的方法,所以一个设计完善的父类是无法对子类做出任何强制性的覆写约定。为了解决这样的…

    2020年12月7日
    1.1K
  • Java自学之String类

    在实际项目开发中,String是一个必须使用的程序类,可以说是项目的核心组成类。在Java程序里所有的字符串都要求使用【’‘】进行定义,同时也可以利用【+】实现字符串的连接处理。 …

    2020年11月30日
    1.2K
  • JAVA学习路线之夯实基础

    第一章 开发环境 JDK(Java SE Development Kit),Java标准版开发包,提供编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行环…

    2020年1月14日
    1.5K
  • spring boot练习篇之用户登录系统【接口篇】

    写在前面 抛弃JSP,只做纯粹的前后端分离项目。 作为一个资深前端工作者,想要转JavaWeb开发,无论是书籍,还是网上视频、资料,竟然没有一篇能清楚明白地讲解怎样搭建一个前后端分…

    2021年5月25日
    1.5K
  • 如何使用IDEA2020.2新建servlet工程

    最近自学java时,发现IDEA更新到2020.2版本时,在新建工程时,有了明显的改动。由于小编刚学到servlet,IDEA这一突然间的改动,导致小编不会新建servlet工程了…

    2020年8月17日
    5.5K
  • Spring Boot的常用注解

    未来的框架趋势是“约定大于配置”,代码的封装会更加严密。开发人员会将更多的精力放在代码的整体优化和业务逻辑上,所以注解式编程会被更加广泛地使用。那么什么是注解?Spring Boo…

    2024年8月29日
    259
  • JAVA学习之多线程知识点整理

    1、什么是进程?什么是线程? 进程是一个应用程序。线程是一个进程中的执行场景或者执行单元。一个进程可以启动多个线程。进程之间内存独立不共享。同一个进程中的线程之间,堆内存和方法区内…

    2020年6月19日
    1.1K
  • Java自学之反射机制

    重用性是面向对象设计的核心原则。为了进一步提升代码的重用性,Java提供了反射机制。反射技术首先考虑的是“反”与“正”的操作,所谓的“正”操作,是指当开发者使用一个类的时候,一定要…

    2020年12月24日
    1.1K
  • STS插件mybatis-generator安装及使用

    断断续续学习Java也有好长时间了,没有师傅带,没有项目练手,学习超级慢,也很烦。视频、书籍翻看了一大推,还是没有目标。 相信滴水成海,外加条条大路通罗马,只要坚持,自己终能达成目…

    2019年12月27日
    2.1K
  • spring boot练习篇之用户登录系统【接入数据库】

    抛弃JSP,只做纯粹的前后端分离项目。 写在前面 学习基础知识是枯燥无味的,之所以会这样,多数是因为心不静,对于如何运用它,感到茫然。所以建议大家在学习Java基础知识的时候,一定…

    2021年5月28日
    1.1K

发表回复

Please Login to Comment