Java泛型简介–第6部分

这是对泛型的介绍性讨论的延续, 此处的先前部分可以在此处找到。

在上一篇文章中,我们讨论了关于类型参数的递归边界。 我们看到了递归绑定如何帮助我们重用了车辆比较逻辑。 在该文章的结尾,我建议当我们不够小心时,可能会发生类型混合。 今天我们将看到一个例子。

如果有人错误地通过以下方式创建了Vehicle的子类,则可能会发生混合:

/*** Definition of Vehicle*/
public abstract class Vehicle<E extends Vehicle<E>> implements Comparable<E> {// other methods and propertiespublic int compareTo(E vehicle) {// method implementation}
}/*** Definition of Bus*/
public class Bus extends Vehicle<Bus> {}/*** BiCycle, new subtype of Vehicle*/
public class BiCycle extends Vehicle<Bus> {}/*** Now this class’s compareTo method will take a Bus type* as its argument. As a result, you will not be able to compare* a BiCycle with another Bicycle, but with a Bus.*/
cycle.compareTo(anotherCycle);  // This will generate a compile time error
cycle.compareTo(bus);    // but you will be able to do this without any error

枚举不会发生这种类型的混淆,因为JVM负责子类化和为枚举类型创建实例,但是如果我们在代码中使用此样式,则必须小心。

让我们谈谈递归边界的另一个有趣的应用。 考虑以下类别:

public class MyClass {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public MyClass() {}public String getAttrib1() {return attrib1;}public void setAttrib1(String attrib1) {this.attrib1 = attrib1;}public String getAttrib2() {return attrib2;}public void setAttrib2(String attrib2) {this.attrib2 = attrib2;}public String getAttrib3() {return attrib3;}public void setAttrib3(String attrib3) {this.attrib3 = attrib3;}public String getAttrib4() {return attrib4;}public void setAttrib4(String attrib4) {this.attrib4 = attrib4;}public String getAttrib5() {return attrib5;}public void setAttrib5(String attrib5) {this.attrib5 = attrib5;}
}

如果我们要创建此类的实例,则可以执行以下操作:

MyClass mc = new MyClass();
mc.setAttrib1("Attribute 1");
mc.setAttrib2("Attribute 2");

上面的代码创建该类的实例并初始化属性。 如果我们可以在此处使用方法链接 ,那么我们可以编写:

MyClass mc = new MyClass().setAttrib1("Attribute 1").setAttrib2("Attribute 2");

显然比第一个版本好得多。 但是,要启用这种方法链接,我们需要通过以下方式修改MyClass

public class MyClass {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public MyClass() {}public String getAttrib1() {return attrib1;}public MyClass setAttrib1(String attrib1) {this.attrib1 = attrib1;return this;}public String getAttrib2() {return attrib2;}public MyClass setAttrib2(String attrib2) {this.attrib2 = attrib2;return this;}public String getAttrib3() {return attrib3;}public MyClass setAttrib3(String attrib3) {this.attrib3 = attrib3;return this;}public String getAttrib4() {return attrib4;}public MyClass setAttrib4(String attrib4) {this.attrib4 = attrib4;return this;}public String getAttrib5() {return attrib5;}public MyClass setAttrib5(String attrib5) {this.attrib5 = attrib5;return this;}
}

然后我们将可以对此类的实例使用方法链接。 但是,如果我们想在涉及继承的地方使用方法链接,那么事情就会变得混乱:

public abstract class Parent {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public Parent() {}public String getAttrib1() {return attrib1;}public Parent setAttrib1(String attrib1) {this.attrib1 = attrib1;return this;}public String getAttrib2() {return attrib2;}public Parent setAttrib2(String attrib2) {this.attrib2 = attrib2;return this;}public String getAttrib3() {return attrib3;}public Parent setAttrib3(String attrib3) {this.attrib3 = attrib3;return this;}public String getAttrib4() {return attrib4;}public Parent setAttrib4(String attrib4) {this.attrib4 = attrib4;return this;}public String getAttrib5() {return attrib5;}public Parent setAttrib5(String attrib5) {this.attrib5 = attrib5;return this;}
}public class Child extends Parent {private String attrib6;private String attrib7;public Child() {}public String getAttrib6() {return attrib6;}public Child setAttrib6(String attrib6) {this.attrib6 = attrib6;return this;}public String getAttrib7() {return attrib7;}public Child setAttrib7(String attrib7) {this.attrib7 = attrib7;return this;}
}/*** Now try using method chaining for instances of Child* in the following way, you will get compile time errors.*/
Child c = new Child().setAttrib1("Attribute 1").setAttrib6("Attribute 6");

这样做的原因是,即使Child从其父级继承了所有的setter,所有这些setter方法的返回类型也都是Parent类型,而不是Child类型。 因此,第一个设置器将返回类型为Parent的引用,调用setAttrib6会导致编译错误,因为它没有任何此类方法。

我们可以通过在Parent上引入通用类型参数并在其上定义递归绑定来解决此问题。 它的所有子项从其扩展时都将自己作为类型参数传递,从而确保setter方法将返回其类型的引用:

public abstract class Parent<T extends Parent<T>> {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public Parent() {}public String getAttrib1() {return attrib1;}@SuppressWarnings("unchecked")public T setAttrib1(String attrib1) {this.attrib1 = attrib1;return (T) this;}public String getAttrib2() {return attrib2;}@SuppressWarnings("unchecked")public T setAttrib2(String attrib2) {this.attrib2 = attrib2;return (T) this;}public String getAttrib3() {return attrib3;}@SuppressWarnings("unchecked")public T setAttrib3(String attrib3) {this.attrib3 = attrib3;return (T) this;}public String getAttrib4() {return attrib4;}@SuppressWarnings("unchecked")public T setAttrib4(String attrib4) {this.attrib4 = attrib4;return (T) this;}public String getAttrib5() {return attrib5;}@SuppressWarnings("unchecked")public T setAttrib5(String attrib5) {this.attrib5 = attrib5;return (T) this;}
}public class Child extends Parent<Child> {private String attrib6;private String attrib7;public String getAttrib6() {return attrib6;}public Child setAttrib6(String attrib6) {this.attrib6 = attrib6;return this;}public String getAttrib7() {return attrib7;}public Child setAttrib7(String attrib7) {this.attrib7 = attrib7;return this;}
}

请注意,我们已经明确地施放T类型,因为编译器不知道这种转换是否是可能的,即使它是因为牛逼的定义是由父<T>界。 同样,由于我们将对象引用转换为T ,因此编译器将发出未经检查的警告。 为了抑制这种情况,我们在设置器上方使用了@SuppressWarnings(“ unchecked”)

经过上述修改,这样做是完全有效的:

Child c = new Child().setAttrib1("Attribute 1").setAttrib6("Attribute 6");

当以这种方式编写方法设置器时,我们应注意不要将递归边界用于任何其他目的,例如从父级访问子级状态,因为这会使父级暴露其子类的内部细节,并最终破坏封装。

通过这篇文章,我完成了泛型的基本介绍。 我在本系列中没有讨论太多的事情,因为我认为它们已经超出了介绍的范围。

直到下一次。

翻译自: https://www.javacodegeeks.com/2014/07/an-introduction-to-generics-in-java-part-6.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

页面监听,一段时间内不操作网页,就自动跳转到登录页

需求&#xff1a;用户在 5 分钟内没有操作网页&#xff0c;就自动跳转到登录页。 环境&#xff1a;jquery 项目&#xff0c;有公共的 js 文件 。 在所有页面都引用的 js 文件中添加下面代码&#xff1a; //判断用户是否在5分钟内未操作页面&#xff0c;如果没有操作&#xff…

类的函数成员的定义无关顺序

类成员变量、成员函数的定义顺序与调用顺序无关如果函数A定义在函数B的后面&#xff0c;但是在函数B也可以直接调用函数A。因为编译器分两步处理类&#xff1a;首先编译成员的声明。然后再编译函数体&#xff08;如果有的话&#xff09;。转载于:https://www.cnblogs.com/AKUN-…

页面刷新 vuex 数据重新被初始化

1、原因 vuex里用来存储的也只是一个全局变量&#xff0c;当页面刷新&#xff0c;该全局变量自然不存在了。 2、解决 使用localStorage存储一份 &#xff08;1&#xff09;storage.js /*** vuex localStorage plugin*/const IS_ALL 0export default function storagePlugi…

小程序 循环中有多个input,怎么获取每个input输入框的值

像上面这样的&#xff0c;使用form表单结构不太合理。官方文档中 wx.createSelectorQuery() 方法我这里总是打印不出值&#xff0c;所以只好通过输入框的事件来做判断&#xff0c;因为提交时&#xff0c;上面所有的输入框都是要有值的&#xff0c;也就意味着这些输入框都会使用…

了解JUnit的Runner架构

几周前&#xff0c;我开始创建一个小的JUnit Runner&#xff08; Oleaster &#xff09;&#xff0c;它允许您使用Jasmine方式在JUnit中编写单元测试。 我了解到&#xff0c;创建自定义JUnit Runners实际上非常简单。 在本文中&#xff0c;我想向您展示JUnit Runner在内部如何工…

C# DateTime 格式

C# DateTime 格式 DateTime dt DateTime.Now;//Label1.Text dt.ToString();//2005-11-5 13:21:25//Label2.Text dt.ToFileTime().ToString();//127756416859912816//Label3.Text dt.ToFileTimeUtc().ToString();//127756704859912816//Label4.Text dt.ToLocalTime().ToStri…

琼瑶哀悼丈夫去世

转载于:https://www.cnblogs.com/gitwow/p/10982911.html

js slice 参数为负值

示例代码 <!DOCTYPE html><html lang"zh"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><meta http-equiv"X-UA-Compatible" con…

vue 简单实用的elementUI表格封装

在写这个表格组件之前&#xff0c;要了解 slot 插槽的使用。 目录 1.子组件&#xff1a;子组件调用父组件的方法 this.$parent.方法名 2.父组件使用 2.1 父组件&#xff08;普通表格&#xff09;&#xff1a; 2.2 父组件&#xff08;表格中的某一项数据需要修改&#xff0…

Java私有,受保护,公共和默认

您是Java程序员&#xff0c;所以您知道我在说什么。 public修饰符使方法或字段可从应用程序中的任何位置访问。 那是简单的部分。 但是&#xff0c;您能告诉我protected包和私有包的区别吗&#xff1f; &#xff08;提示&#xff1a;当您不在方法或字段前面编写任何访问修饰符时…

vue vuex 大型项目demo示例

1、vuex 动态模块配置 import Vue from vueimport Vuex from vueximport store from /store;// 使用Vuex插件&#xff0c;即使插件被调用多次&#xff0c;插件也只会安装一次Vue.use(Vuex);// state存储数据的状态const state {// 数据状态name: mfg}// getters获取数据状态co…

【学习】012 垃圾回收机制算法分析

垃圾回收机制概述 Java语言中一个显著的特点就是引入了垃圾回收机制&#xff0c;使c程序员最头疼的内存管理的问题迎刃而解&#xff0c;它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制&#xff0c;Java中的对象不再有“作用域”的概念&#xff0…

js 计算对象数组中某个属性值重复出现的个数

转载于&#xff1a;前进中的蜗牛 let list [{name:"1000积分",serialNumber:6},{name:"500积分",serialNumber:7},{name:"50积分",serialNumber:8},{name:"50积分",serialNumber:8},{name:"50积分",serialNumber:8} ];//怎…

介绍JBoss BPM Suite安装程序

本周&#xff0c;我们想向您介绍JBoss BRMS和JBoss BPM Suite产品随附的一个鲜为人知的安装程序组件。 请注意&#xff0c;当前所有的演示项目都要求您下载JBoss BPM Suite可部署的eap zip产品文件和JBoss EAP 6.1.1 zip产品文件。 展望未来&#xff0c;我们将迁移所有项目&am…

小程序 报错errMsg: “hideLoading:fail:toast can‘t be found“ ?

小程序体验版上/真机上报错&#xff1a;errMsg: "hideLoading:fail:toast cant be found" &#xff1f;模拟器上却看不到&#xff0c; wx.showLoading 和 wx.showToast 同时只能显示一个&#xff1b;wx.showLoading 应与 wx.hideLoading 配对使用&#xff1b;把请求…

世界名牌大学课件下载地址

一、加州大学伯克利分校 http://webcast.berkeley.edu/courses.php 作为美国第一的公立大学&#xff0c;伯克利分校提供了许多优秀教授的播客和视频讲座&#xff0c;可以跟踪最新的讲座。想看教授布置的作业和课堂笔记&#xff0c;可以点击该教授的网页&#xff0c;通常&#…

jquery-deferred应用

我们说jquery1.5之后用的用deferred&#xff0c;那么deferred到底是个什么东西&#xff0c;看个例子var wait function(){var task function(){console.log(执行完成);}setTimeout(task, 2000); } wait(); 现在在task里面的执行完成之后进行某些特别复杂的操作&#xff0c;代…

自动装箱,拆箱和NoSuchMethodError

J2SE 5为Java编程语言引入了许多功能。 这些功能之一是自动装箱和拆箱 &#xff0c;这是我几乎每天都没有考虑过的功能。 它通常很方便&#xff08;尤其是与收藏夹一起使用时&#xff09;&#xff0c;但有时会导致一些令人讨厌的惊喜 &#xff0c;即“ 怪异 ”和“ 疯狂” 。 在…

vue 不能监测数组长度变化length的原因

由于 JavaScript 的限制&#xff0c;Vue 不能检测以下变动的数组&#xff1a; 当你利用索引直接设置一个项时&#xff0c;例如&#xff1a; vm.items[indexOfItem] newValue 当你修改数组的长度时&#xff0c;例如&#xff1a; vm.items.length newLength因为vue的响应式是通…

vue 动态显示三级路由

无需 vuex、本地存储实现动态显示三级路由。 目录 一、需求描述&#xff1a; 二、代码 2.1 路由配置 1. 我的一级菜单和二级菜单的路由配置的&#xff1a; 2. 三级菜单的路由配置&#xff1a; 3. 上面有几个变量和要注意的细节&#xff1a; 2.2 封装导航栏 2.3 封装面…