当前位置: 首页 > news >正文

古风网站建设模板更改wordpress小工具的样式

古风网站建设模板,更改wordpress小工具的样式,网站制作公司网站建设公司,德州金航网站建设泛型的定义 泛型类的定义 下面定义了一个泛型类 Pair#xff0c;它有一个泛型参数 T。 public class PairT {private T start;private T end; }实际使用的时候就可以给这个 T 指定任何实际的类型#xff0c;比如下面所示#xff0c;就指定了实际类型为 LocalDate…泛型的定义 泛型类的定义 下面定义了一个泛型类 Pair它有一个泛型参数 T。 public class PairT {private T start;private T end; }实际使用的时候就可以给这个 T 指定任何实际的类型比如下面所示就指定了实际类型为 LocalDate泛型给了我们一个错觉就是通过个这个模板类 PairT我们可以在实际使用的时候动态的派生出各种实际的类型比如这里的 PairLocalDate 类。 PairLocalDate period new Pair();泛型类的继承 子类是一个泛型类的定义方法如下 public class IntervalT extend PairT {}这里的 IntervalT 类是一个泛型类也可以像上面使用 PairT 类一样给它指定实际的类型。 子类是一个具体类的定义方法如下 public class DateInterval extends PairLocalDate {}这里的 DateInterval 类就是一个具体的类而不再是一个泛型类了。这里的语义是 DateInteral 类继承了 PairLocalDate 类这里的 PairLocalDate 类也是一个具体类。但是由于 Java 的泛型实现机制这里会带来多态上的一个问题见下面的分析。 而像下面的这种定义具体类的写法是错误的 public class DateIntervalLocalDate extends PairLocalDate {}泛型方法的定义 泛型方法定义时类型变量放在修饰符的后面返回值的前面。泛型方法既可以泛型类中定义在普通类中定义。 public static T T genericMethod(T a) {}这里顺便记录一下因为是使用擦除来实现的泛型因此字节码中的方法的签名是不会包含泛型信息的。对于泛型方法会多生成一个 Signature 的属性用于记录方法带泛型信息的签名反编译器也可以根据这个信息将泛型方法还原回来。 构造函数泛型 下面的代码定义了一个泛型类 ConstructorGeneric它的泛型参数是 T这个类的构造函数也是泛型的它有一个泛型参数 X。 class ConstructorGenericT {public X ConstructorGeneric(X a) {} }创建该对象的代码如下 ConstructorGenericNumber t new StringConstructorGenericNumber(123);这里 new 后面的 String 是传给构造器的泛型 X 的即 X 的实际类型为 String类的范型参数是由 Number 传递的即 T 的实际类型是 Number。这里两个都是省略写在这里是为了显示区分出两个参数传递的位置。 类型变量的限定 带单个上界限定 下面的代码定义了一个 NatualNumber 类它的泛型参数 T 限制为 Integer 或者 Integer 的子类。 public class NaturalNumberT extends Integer {private T n;public NaturalNumber(T n) { this.n n; }public boolean isEven() {return n.intValue() % 2 0;} }调用代码如下 // 正常 NaturalNumberInteger natural1 new NaturalNumber(1); // 无法编译因为这里和泛型类定义的上界不符合 NaturalNumberDouble natualral2 new NaturalNumber(1.0);带多个上界的限定 多个上界之间使用 符号进行分隔如果多个限定中有类则类需要排在接口后面因为 Java 不支持多继承所以不存在有多个限定的类的情况。使用时需要满足所有的限定条件才能执行这个校验应该是在编译时期做的因为擦除之后只会保留第一个限定界。 class A {} interface B {} class C extends A implements B {} public static T extends A B void test(T a) {}public static void main(String[] args) {// 编译错误A 只能满足一个上界test(new A());// 正常test(new C()); }通配符 在泛型中使用 ? 表示通配符它的语义是表示未知的类型。通配符可以用作方法的形参、字段的定义、局部变量的定义以及有的时候作为函数的返回值。通配符不能作为实参调用泛型方法不能创建对象或者派生子类型。 上界通配符 当你想定义一个普通方法这个普通方法可以处理某一类的 List 中的元素时比如像ListNumberListIntegerListDouble 时这个时候如果你把方法的入参定义为 ListNumber 是不行的因为在 Java 中 ListInteger 不是 ListNumber 的子类。 public static void process(ListNumber numbers) {}// 编译错误 ListNumber numbers new ArrayList(); proess(numbers);假设 List 是 List 的子类则可以实现如下的代码 ListInteger integers new ArrayList();// 假设下面是成立的 ListNumber numbers integers;// 下面这句也应该是合法的但是这违背了 intergers 只能存放 Integer 的语义 numbers.add(new Double());从上面的例子可以看出如果允许 ListInteger 是 ListNumber 的子类型则会破坏泛型的语义因此这在 Java 中是不允许的。 但是又实际存在上面描述的这种需求因此 Java 提供了上界通配符的语法则方法定义可以定义为如下 public static void process(List? extends Number numbers) {for (Number num : numbers) {// do something} }// 下面的调用都是能够正常编译通过的 ListNumber numbers new ArrayList(); process(numbers);ListInteger integers new ArrayList(); process(integers);ListDouble doubles new ArrayList(); process(doubles);这里的 ? extends Number 的语义就是可以匹配 Number 或者 Number 子类的 List需要注意的是在 Java 中的继承extends和实现implements在这里都用关键字 extends 来表示。 从这里也可以看出List? extends Number 的返回值是可以赋值给 Number 类型的。这里可以想象一下 List 的 get() 方法的泛型参数 E 就变成了 ? extends Number 这个实际类型而它表达的语义是 Number 以及 Number 的子类因此赋值给一个 Number 类型的变量是合法的。 但是下面的代码是不合法的 public static void process(List? extends Number numbers) {numbers.add(new Integer()); }这里同样可以想象一下 List 的 add() 方法的入参的泛型参数 E 就变成了 ? extends Number 这个实际类型它表达的语义是 Number 以及 Number 的子类但是具体是哪个子类是无法确定的。上面的例子也解释了它可能是 NumberIntegerDouble 等假设它是 Double 类型这里放一个 Integer 类型又违背了泛型只能放 Double 的语义因此这里的赋值是不合法的。 无界通配符 下面的代码就是定义了一个 List? 形参的方法这里的 List? 语义是一个未知类型的 List。 public static void printList(List? list) {}无界通配符定义的 List 里面的元素只能赋值给 Object 类型。这里可以想象一下 List 的 get() 方法的泛型参数 E 就变成了 ? 这个实际类型它的语义是一个未知的类型既然是一个未知的类型那么我只能赋值给 Object 类型的变量了。 public static void printList(List? list) {for (Object obj : list) {// do something} }无界通配符定义的 List 里面只能添加 null不能添加其它的任何类型的元素即使是 Object 也不行因为添加了之后就会违背泛型的语义了。 无界通配符的主要使用场景是 需要使用 Object 类中的方法使用了泛型类中不用关心泛型的方法比如 List 中的 size() clear() 方法 下界通配符 在使用上面的上界通配时发现了一个问题如果一个 List 类型形参声明为了上界通配符是没有办法往这个 List 里面添加元素的为了解决这个问题可以使用下界通配符可以定义如下的方法 public static void addNumbers(List? super Number list) {list.add(new Integer());list.add(new Double()); }这里可以想象一下这个时候 List 的 add() 方法的入参的泛型参数 E 就变成了 ? super Integer 类型它的语义是匹配 Number 以及 Number 类型的超类。根据 Java 多态的原理这里实际可以传递的类型为 Integer 以及 Integer 的子类型因为形参声明的是超类实际传递子类的引用当然是合法的。 泛型继承关系 泛型的继承关系如下图所示 通配符捕获 假设定义了一个无界限通配符的方法如下这个方法会编译错误因为按照之前分析的 List? 中不能添加任何类型的对象而这里 list.get(0) 返回的是 Object 类型的对象肯定是无法放入进去的。代码如下 public void foo(List? list) {list.set(0, list.get(0)); // 编译报错 }为了解决这个问题这个时候就可以通过新建一个私有的泛型方法来帮助捕获通配符的类型这个私有的泛型方法名称通常是原有方法加上Helper后缀这种技巧称为通配符捕获。代码如下 pulic void foo(List? list) {// 调用这个方法的语义是告诉编译器我不知道具体类型是什么// 但是取出来和放进去的元素类型是相同的fooHelper(list); }private T void fooHelper(ListT list) {// 合法T temp list.get(0);// 合法list.set(0, temp); }对于泛型方法因为 add() 方法的入参get() 方法返回值的泛型参数都是 T当传入一个 List 进来虽然这个 List 里面的对象实际类型不知道但是通过泛型参数可以判断 get() 方法返回类型和 add() 方法的入参类型都是一样的都是 T 捕获到的一个实际类型 X。 对于带通配符参数的方法因为方法的声明没有一个泛型参数不能捕获到实际的参数类型 X。那么对于每次方法的调用编译器都会认为是一个不同的类型。比如编译器编译的时候 list.set(0, xxx)这里的入参的类型就会是 CAP#1 list.get(0) 返回的类型就是 CAP#2因为没有一个泛型参数来告诉编译器说 CAP#1 和 CAP#2 是一样的类型因此编译器就会认为这两个是不同的类型从而拒绝编译。下图是编译器实际的提示信息 从上面的图也可以看出第二次调用方法时类型又变成 CAP#3 和 CAP#4 了这也证明了每次编译器都会认为是一个新的类型。 实际上这里也可以将这个私有的 Helper 方法定义为公共的然后去掉通配符的方法。这两种定义实际上是达到了相同的效果但是 Java 语言规范 5.1.10 章节中更推荐采用通配符的方式定义但它上面阐述的原因没太看懂但是在另外一篇博客里面看到一个观点感觉有点道理。 它说如果定义成一个泛型方法那么老的遗留的没有用泛型的代码调用这个方法就会产生一个警告但是如果是使用通配符则不会有警告产生。 public static void foo1(List?) {}public static T void foo2(ListT) {}// 假设老的代码没有用泛型 List rawList Arrays.asList(1, 2); // 不会产生告警 foo1(rawList); // 会产生告警提示未经检查的转换 foo2(rawList);然而实际上 JDK 中真正的实现并没有采用这种方式而是直接用注解忽略了异常直接用的原生类型来实现的。Collections 中的 reverse() 方法内部实现逻辑如下 SuppressWarnings({rawtypes, unchecked}) public static void reverse(List? list) { int size list.size(); if (size REVERSE_THRESHOLD || list instanceof RandomAccess) { for (int i0, midsize1, jsize-1; imid; i, j--) swap(list, i, j); } else { // instead of using a raw type here, its possible to capture // the wildcard but it will require a call to a supplementary // private method ListIterator fwd list.listIterator(); ListIterator rev list.listIterator(size); for (int i0, midlist.size()1; imid; i) { Object tmp fwd.next(); fwd.set(rev.previous()); rev.set(tmp); } } }桥接方法 假设定义了如下代码 public class NodeT {public T data;public Node(T data) { this.data data; }public void setData(T data) {System.out.println(Node.setData);this.data data;} }public class MyNode extends NodeInteger {public MyNode(Integer data) { super(data); }public void setData(Integer data) {System.out.println(MyNode.setData);super.setData(data);} }泛型擦除后的实际代码如下注意看 MyNode 里面的 setData() 方法并没有重写 Node 里面的 setData() 方法了因为方法签名不一样。这就违背了 Java 多态的语义。 Java 编译器在编译的时候会自动给 MyNode 生成一个桥接方法这个方法的签名和 Node 类里面的一样然后在这个方法里面去调用真正的 setData() 方法。 通过查看 MyNode.class 文件可以看到真的有两个 setData() 方法存在。 方法的形参类型是 Object 类型和 Node 类中泛型擦除后的类型相同说明这个方法才是真正重载了 Node 类中的方法。 方法实现中调用了 MyNode 类中形参为 Integer 类型的 setData() 方法。 同时在 MyNode 类中不允许自己定义形参为 Object 类型的 setData() 方法了如果定义了则无法编译 经过编译器编译后的代码等效为如下的代码 public class Node {public Object data;public Node(Object data) { this.data data; }public void setData(Object data) {System.out.println(Node.setData);this.data data;} }public class MyNode extends NodeInteger {public MyNode(Integer data) { super(data); }public void setData(Integer data) {System.out.println(MyNode.setData);super.setData(data);}// 由编译器生成的桥接方法// 如果手动定义了这个方法编译器就会报错了public void setData(Object data) {setData((Integer) data);} }泛型的局限性 泛型不能用于基本类型 泛型是通过擦除实现的擦除之后 ArrayList 内部是 Object[] 类型的数组是不能存放基本类型的因为基本类型不是 Object 类型的子类。 Listint list new ArrayList();不能创建泛型类型的实例 泛型是通过擦除来实现的所以擦除之后都会变成 new Object() (没有指定上界的情况)而实际上我们是要创建 T 类型的实例的。 public static T void test(ListT list) {E ele new E();list.add(ele); }// 可以通过如下方式 public static T void test(ListT list, ClassT clazz) {E ele clazz.newInstance();list.add(ele); } // 调用 ListString list new ArrayList(); test(list, String.class);不能声明静态的泛型变量 泛型相当于是类的工厂可以创建不同类型的实例。而静态变量是所有实例共享的如果允许声明静态的泛型变量那么不同类型的实例之间就会存在矛盾。 public class MobileDeviceT {private static T os; }// 这两个实例的静态变量就会存在矛盾 MobileDeviceSmartphone phone new MobileDevice(); MobileDeviceTabletPC pc new MobileDevice();不能使用 instanceof 判断泛型类型 泛型是通过擦除实现的因此 ListT.class 在内存中是不存在的只有 List.class这个类型也被称为原生类型。 // 错误 if (list instanceof ListString) { }// 正确 if (list instanceof List) { }不能创建泛型数组 泛型是通过擦除实现的如果允许声明泛型数组则无法实现数组在存放时会校验数组的元素类型这个语义。 // 假设允许创建这个数组的每个元素只允许存放 ListString 类型的元素 Object[] stringLists new ListString[2]; // 正确执行 stringLists[0] new ArrayListString(); // 这行应该抛出 ArrayStoreException 异常 // 但是由于擦除实际上和上面是一样的这里违背了数组的语义 stringLists[1] new ArrayListInteger();不能创建、捕获、抛出带泛型的异常 // 编译报错 class MathExceptionT extends Exception {} // 编译报错 class QueueFullExceptionT extends Throwable {}// 编译报错 public static T extends Exception, J void execute(ListJ jobs) {try {for (J job : jobs)} catch (T e) { // 编译报错} }class ParserT extends Exception {// 这样是允许的// 我觉得允许的原因是声明了抛出父类而实际抛出子类也是合法的public void parse(File file) throws T { } }不能使用擦除后原生类型相同的泛型参数方法来重载 public class Example {// 这两个方法擦除后的参数是一样的所以不能算重载public void print(SetString strSet) { }public void print(SetInteger intSet) { } }堆污染 当定义变长的泛型参数时如果尝试把一个原生类型赋值给变成泛型参数就有可能发生堆污染。堆污染的本质原因就是可以通过语法糖变长参数列表来创建泛型的的数组导致的。例如下面的代码 public class ArrayBuilder {public static T void addToList (ListT listArg, T... elements) {for (T x : elements) {listArg.add(x);}}public static void faultyMethod(ListString... l) {// 这里编译应该会有告警如果忽略这个告警则有可能带来堆污染Object[] objectArray l; objectArray[0] Arrays.asList(42);String s l[0].get(0); } }编译告警中就会提示有堆污染如下图所示 当编译器遇到一个变长参数方法时它会把它转换为一个数组。对于 T... elements 这种参数声明就会转为 T[] elements因为泛型的擦除最终会被转换为 Object[] elements这里编译器就会认为有可能发生堆污染。 可以通过以下三种方式抑制这种警告 SuppressWarnings({unchecked, varargs}) 这种方式只能抑制方法声明时候的告警方法调用处还是会产生告警 SafeVarargs 不会产生任何警告 增加 -Xlint:varags 编译选项 不会产生任何警告 JVM 控制参数 显示所有告警信息 给编译器增加 -Xlint:unchecked 在 Idea 中可以参考如下图配置 显示更详细的诊断信息 给编译增加 -Xdiags: verbose 选项 显示所有告警信息为英文 增加如下环境变量 Idea 中可以将配置放在 vmproperties 文件中如下图所示 参考 Java Generic Tutorial Java核心技术·卷 I原书第10版 深入理解Java虚拟机第3版 When to use generic methods and when to use wild-card? Why use a wild card capture helper method? Capture Conv: rev/reverse - what’s the point? Difference between ? super T and ? extends T in Java What is PECS (Producer Extends Consumer Super)? Differences between copy(List? super T dest, List? extends T src) and copy(List dest, List? extends T src)
http://www.w-s-a.com/news/647061/

相关文章:

  • 检察院门户网站建设情况总结深圳网站制作长沙
  • 单页导航网站模板搜索量查询
  • 如何在一个地方建设网站营销型定制网站
  • 保定网站建设方案维护动易网站中添加邮箱
  • 简易网站的html代码wordpress音乐html
  • 四川住房和城乡建设厅网站打不开海山网站建设
  • 深圳设计功能网站如何用html制作网站
  • 网络优化软件下载竞价排名和seo的区别
  • 龙华新区做网站中高端网站建设
  • 网站开发小图标大全手机网站设计开发
  • 网页设计设计一个网站口碑营销的优点
  • 枣庄建网站的公司唐山企业网络推广培训
  • 张家界建设企业网站学校资源网站建设方案
  • 网站制作教程书籍业务管理系统
  • 上传网站空间的建站程序怎么删除c 网站开发案例详解下载
  • 企业网站维护兼职丹阳网站优化
  • 秦皇岛网站开发公司怎么注册自己的公司
  • 写作网站哪个能得稿费绿色环保企业网站模板
  • 牡丹江网站建设定制开发安徽建设工程信息网官网入口
  • 有什么好的网站建设的书适合在家做的网站工作
  • wordpress情侣源码西安网站快速优化
  • 昆明网站建设高端定制100种班服设计图
  • 网站开发程序说明html网页制作接单
  • 企业网站货物查询怎么做制作文件的软件
  • 怎么做网站的防盗链北京门户企业网站建设
  • 网站推广的主流方法淘客网站 源码
  • 网站海外推广怎么做多用户商城系统源码教程
  • 猎头做单网站网站创建费用
  • 住房和城乡建设网站 上海自己做网站还是公众号
  • 投票网站怎么制作电商网站模板html