java基础-泛型举例详解

泛型

  泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数。这种类型参数可以在类、接口、和方法的创建中,分别被称为泛型类、泛型接口、泛型方法。

一、认识泛型

  在没有泛型之前,通过对类型Object的引用来实现参数的"任意化",但"任意化"带来的缺点是需要显示的强制类型转换,此种转换要求开发者对实际参数类型预知的情况下进行,对于强制转换错误的情况,编译器可能不会提示错误,但在运行时会出现异常,这是一个安全隐患。

  举例:不使用泛型实现参数化类型

 1 package generic;
 2 
 3 public class NoGeneric {
 4     private Object ob;    //定义通用类型成员
 5     public NoGeneric(Object ob) {
 6         this.ob = ob;
 7     }
 8     public Object getOb() {
 9         return ob;
10     }
11     public void setOb(Object ob) {
12         this.ob = ob;
13     }
14     public void showType() {
15         System.out.println("实际类型是:"+ob.getClass().getName());
16     }
17 }
18 
19 package generic;
20 
21 public class NoGenericDemo {
22 
23     public static void main(String[] args) {
24         // TODO 自动生成的方法存根
25         //定义类NoGener的一个Integer版本
26         NoGeneric intob = new NoGeneric(new Integer(66));
27         intob.showType();
28         int i = (Integer)intob.getOb();
29         System.out.println("value="+ i);
30         System.out.println("-----------------------------");
31         //定义类NoGeneric的一个String版本
32         NoGeneric strob = new NoGeneric(new String("hello"));
33         strob.showType();
34         String s = (String)strob.getOb();
35         System.out.println("value="+ s);
36     }
37 }

   执行结果为:

实际类型是:java.lang.Integer
value=66
-----------------------------
实际类型是:java.lang.String
value=hello

 

 

   上面的实例有两点需要注意:首先如下语句

String s = (String)strob.getOb();

 

  在使用时必须明确指定返回对象需要被强制转化的类型为String,否则无法编译通过;其次,由于intob和strob都属于NoGeneric的类型,假如执行如下语句

intob = strob;

 

  此种赋值,语法上是合法的,而在语义上是错误的,对于这种情况,只有在运行时才会出现异常,使用泛型就不会出现上述错误,泛型的好处就是在编译期 检查类型,捕捉类型不匹配错误,并且所有强制转换都是自动和隐式的,提高代码的重用率.

  举例 2:使用泛型使用泛型实现参数实例化类型

package generic;public class Generic<T> {private T ob; //定义泛型成员变量 public Generic(T ob) {this.ob = ob;}public T getOb() {return ob;}public void setOb(T ob) {this.ob = ob;}public void showType() {System.out.println("实例类型为:" + ob.getClass().getName());}
}package generic;public class GenericDemo {public static void main(String[] args) {// TODO 自动生成的方法存根//定义泛型Generic的一个Integer的版本Generic<Integer> intob = new Generic<Integer>(88);intob.showType();int i = intob.getOb();System.out.println("value=" + i);System.out.println("----------------------");//定义泛型Generic的一个String版本Generic<String> strob = new Generic<String>("hello");strob.showType();String s = strob.getOb();System.out.println("value=" + s);}
}

  运行结果为:

实例类型为:java.lang.Integer
value=88
----------------------
实例类型为:java.lang.String
value=hello

 

  在引入泛型的前提下,如果再次执行

intob = strob;

 

  将提示错误,编译无法通过

二、泛型定义

  泛型的语法可归纳为

class class-name <type-param-list>{//......}

 

  实例化泛型的语法为:

class-name <type-param-list> obj =  new class-name<type-param-list>(cons-arg-list);

 

  type-param-list用于指明当前泛型类可接受的类型参数占位符的个数;   如:

class Generic<T>{//......}

 

  这里的T是类型参数的名称,并且只允许传一个类型参数给Generic类,在创建对象时,T用作传递 给Generic的实际类型的占位符,每当声明类型参数时,只需用目标类型替换T即可.   如:

Generic <Integer> intob; 

 

  声明对象时占位符T用于指定实际类型,如果传递实际类型为Integer,属性ob就是Integer类型,类型T还可以指定方法的返回类型     如:

public T getOb(){return ob;
}

   理解泛型有三点需要注意:

    1、泛型的类型参数只能为类类型(包括自定义类),不能是基本数据类型。

    2、同一种泛型可以对应多个版本(因为类型参数时不确定的)、不同版本的泛型类实例是不兼容的。

    3、泛型的类型参数可以有多个。

    注意   根据惯例,泛型类定义时通常使用一个唯一的大写字母表示一个类型参数.

三、有界类型

   定义泛型类时,可以向类型参数指定任何类型信息,特别是集合框架操作中,可以最大限度地提高适用范围,但有时候需要对类型参数的取值进行一定程度的限制,以使数据具有可操作性.

   为了处理这种情况,java提供了有界类型,.在指定类型参数时可以使用extends关键字限制此类型参数代表的类必须是继承自指定父类或父类本身.

  使用extends关键字实现有界类型泛型类的定义

package generic;public class BoundGeneric<T extends Number> {//定义泛型数组
    T[] array;public BoundGeneric(T[] array) {this.array = array;}//计算总和public double sum() {double sum = 0.0;for(T t : array) {sum = sum + t.doubleValue();}return sum;}
}

 

  BoundGeneric类的定义中,使用extends将T的类型限制为Number类及其子类,故可以再定义过程中调用Number类的doubleValue方法,现在分别指定Integer,double,String类型作为类型参数,测试BoundGeneric:

package generic;public class BoundGenericDemo {public static void main(String[] args) {// TODO 自动生成的方法存根//使用整形数组构造泛型对象Integer[] intArray = {1, 2, 3, 4};BoundGeneric<Integer> iobj = new BoundGeneric<Integer>(intArray);System.out.println("iobj的和为:" + iobj.sum());//使用Double型数组构造泛型对象Double[] douArray = {1.2, 2.3, 3.4, 4.5};BoundGeneric<Double> dobj = new BoundGeneric<Double>(douArray);System.out.println("dobj的和为:" + dobj.sum());String[] strArray = {"str1","str2"};//下面的语句将会报错,String不是Number的子类//BoundGeneric<String> sobj = new BoundGeneric<String>(strArray);
    }
}

 

  运行结果为:

iobj的和为:10.0
dobj的和为:11.4

  注:在使用extends(如:T extends someClass)声明的泛型类进行实例化时允许传递的参数类型为:如果someClass是类,可以传递someClass本身及其子类;如果someClass接口可以传递实现接口的类

四、通配符

  首先在说通配符之前先看一下这段代码:使用前面定义的Generic类.

package generic;public class WildcarDemo {public static void func(Generic <Object> g) {//...
    }public static void main(String args[]) {Generic <Object> obj = new Generic<Object>(12);func(obj);Generic<Integer> iobj = new Generic<Integer>(12);//这里讲产生一个错误:类型 WildcarDemo 中的方法 func(Generic<Object>)对于参数(Generic<Integer>)不适用//func(iobj);
    }
}

 

  上述代码的func()方法的创建意图是能够处理各种类型参数的Generic对象,因为Generic是泛型,所以在使用时需要为其指定具体的参数化类型Object,看似不成问题,

  但在

func(iobj);

 

  处产生一个编译错误,因为func定义过程中以明确声明的Generic的类型参数为Object,这里试图将Generic<Integer>类型的对象传递给func()方法,类型不匹配导致编译错误.这种情况可以使用通配符解决.通配符由"?"来表示,它代表一个未知类型

package generic;public class WildcarDemo2 {public static void func(Generic <?> g) {//...
    }public static void main(String args[]) {Generic<Object> obj = new Generic<Object>(12);func(obj);Generic<Integer> iobj = new Generic<Integer>(12);func(iobj);}
}

 

  上述代码,在采用了通配符后语句将无误的编译,运行.

  在通配符使用的过程中,也可通过extends关键字限定通配符的界定的类型参数的范围.

package generic;public class WildcarDemo3 {public static void func(Generic <? extends Number> g) {//...
    }public static void main(String args[]) {Generic<Object> obj = new Generic<Object>(12);//这里将产生一个错误:类型 WildcarDemo3 中的方法 func(Generic<? extends Number>)对于参数(Generic<Object>)不适用//func(obj);
        Generic<Integer> iobj = new Generic<Integer>(12);func(iobj);}
}

 

五、泛型的局限性

  java并没有真正实现泛型,是编译器在编译的时候在字节码上做了手脚(称为擦除). 这种实现理念在成java泛型本身有很多漏洞, 为了避免这些问题java对泛型的使用上做了一些约束,但不可避免的还是有一些问题存在.多数的限制都是由类型擦除引起的.

  1、泛型类型不能被实例化

public class Gen<T>{T ob;public Gen(){ob = new T();}    
}

 

  Gen<T>构造器是非法的,类型擦除将变量T替换成Object,但这段代码的本意肯定不是调用new Object().类似:如

public <T> T[]build (T[] a){T [] array = new T[2];  
  //... }

 

  类型擦除会让这个方法总是构造一个Object[2]数组,但是可以通过调用Class.newInstance和Array.newInstance方法,利用反射构造泛型对象和数组

2、数组

  不能实例化数组如:

T[] vals;
vals = new T[10];

 

  因为T在运行时时不存在的,编译器无法知道实际创建那种类型的数据.

  其次,不能创建一个类型特定的泛型引用的数组    如:

Gen<String> []arrays = new Gen<String>[100];

 

  上面的代码会损害类型安全

  如果使用通配符,就可以创建泛型类型的引用数组

Gen<?> []arrays = new Gen<?>[10];

 

3、怒能用类型参数替换基本类型

  因为擦除类型后原先的类型参数被Object或者限定类型替换,而基本类型是不能被对象所存储的,可以使用基本类型的包装类来解决此问题

4、异常

  不能抛出也不能捕获泛型类的异常对象,使用泛型类来扩展Throwable也是非法的. 如:

public class GenericException <T> extends Exception{//泛型类无法继承Throwable
}

 

  不能再catch子句中使用类型参数,例如下面的方法将不能编译

    public static <T extends Throwable> void doWork(Class<T> t) {try {//...}catch(Throwable realCause) {//...}

 

  但是在异常声明时可以使用类型参数,如:

    public static <T extends Throwable> void doWork(T t) throws T {try {//...}catch(Throwable realCause) {throw t;}}

 

5、静态成员

  不能在静态变量或者静态方法中引用类型参数  如:

public class Gen<T>{static T ob;static T getOb() {return ob;}
}

 这些均参考自“Java SE程序设计”,算是做个笔记,以后忘了可以翻阅一下,写在自己的随笔中,也希望可以帮助更多的人。如有侵权,请联系本人删除

 

转载于:https://www.cnblogs.com/Mykebai/p/9123749.html

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

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

相关文章

MySQL数据库视图(view),视图定义、创建视图、修改视图

原文链接&#xff1a;https://blog.csdn.net/moxigandashu/article/details/63254901转载于:https://www.cnblogs.com/chrdai/p/9131881.html

[pytorch、学习] - 3.10 多重感知机的简洁实现

参考 3.10. 多重感知机的简洁实现 import torch from torch import nn from torch.nn import init import numpy as np import sys sys.path.append("..") import d2lzh_pytorch as d2l3.10.1. 定义模型 num_inputs, num_outputs, num_hiddens 784, 10, 256 # 参…

【汇编语言】——第三章课后总结

第三章 的书本上主要有以下几个内容&#xff1a; 1.内存中字的存储 字单元&#xff1a;即存放一个字型数据&#xff08;16位&#xff09;的内存单元&#xff0c;由两个地址连续的内存单元组成。 小端法&#xff1a;高地址内存单元中存放字型数据的高位字节&#xff0c;低地址内…

如何从 Android 手机免费恢复已删除的通话记录/历史记录?

有一个有合作意向的人给我打电话&#xff0c;但我没有接听。更糟糕的是&#xff0c;我错误地将其删除&#xff0c;认为这是一个骚扰电话。那么有没有办法从 Android 手机恢复已删除的通话记录呢&#xff1f;” 塞缪尔问道。如何在 Android 上恢复已删除的通话记录&#xff1f;如…

springBoot 登录拦截器

1、首选创建一个继承HandlerInterceptor的拦截器 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /*** 拦…

[pytorch、学习] - 3.11 模型选择、欠拟合和过拟合

参考 3.11 模型选择、欠拟合和过拟合 3.11.1 训练误差和泛化误差 在解释上述现象之前&#xff0c;我们需要区分训练误差&#xff08;training error&#xff09;和泛化误差&#xff08;generalization error&#xff09;。通俗来讲&#xff0c;前者指模型在训练数据集上表现…

关于'java' 不是内部或外部命令,也不是可运行的程序 或批处理文件 和 错误: 找不到或无法加载主类 helloworld的问题...

一、前几天电脑重装了一次系统将java配置的环境变量都弄没了&#xff0c;自己添加了两个新的变量JAVA_HOME&#xff08;自己jdk的地址&#xff09;以及在path中添加%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 然后因为这几天都是用eclipse进行编程的&#xff0c;没有出现问题&#…

spring-boot注解详解(一)

spring-boot注解详解(一) SpringBootApplication SpringBootApplication (默认属性)Configuration EnableAutoConfiguration ComponentScan。 Configuration&#xff1a;提到Configuration就要提到他的搭档Bean。使用这两个注解就可以创建一个简单的spring配置类&#xf…

前端基础-jQuery的优点以及用法

一、jQuery介绍 jQuery是一个轻量级的、兼容多浏览器的JavaScript库。jQuery使用户能够更方便地处理HTML Document、Events、实现动画效果、方便地进行Ajax交互&#xff0c;能够极大地简化JavaScript编程。它的宗旨就是&#xff1a;“Write less, do more.“二、jQuery的优势 一…

[pytorch、学习] - 3.12 权重衰减

参考 3.12 权重衰减 本节介绍应对过拟合的常用方法 3.12.1 方法 正则化通过为模型损失函数添加惩罚项使学出的模型参数更小,是应对过拟合的常用手段。 3.12.2 高维线性回归实验 import torch import torch.nn as nn import numpy as np import sys sys.path.append("…

Scapy之ARP询问

引言 校园网中&#xff0c;有同学遭受永恒之蓝攻击&#xff0c;但是被杀毒软件查下&#xff0c;并知道了攻击者的ip也是校园网。所以我想看一下&#xff0c;这个ip是PC&#xff0c;还是路由器。 在ip视角&#xff0c;路由器和pc没什么差别。 实现 首先是构造arp报文&#xff0c…

spring-boot注解详解(二)

ResponseBody 作用&#xff1a; 该注解用于将Controller的方法返回的对象&#xff0c;通过适当的HttpMessageConverter转换为指定格式后&#xff0c;写入到Response对象的body数据区。使用时机&#xff1a; 返回的数据不是html标签的页面&#xff0c;而是其他某种格式的数据时…

转:org.apache.maven.archiver.MavenArchiver.getManifest错误

eclipse导入新的maven项目时&#xff0c;pom.xml第一行报错&#xff1a; org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration) 解决办法&#xff1a; 1、Help——>Install …

Codeforces Round #524 Div. 2 翻车记

A&#xff1a;签到。room里有一个用for写的&#xff0c;hack了一发1e8 1&#xff0c;结果用了大概600ms跑过去了。惨绝人寰。 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorith…

[pytorch、学习] - 3.13 丢弃法

参考 3.13 丢弃法 过拟合问题的另一种解决办法是丢弃法。当对隐藏层使用丢弃法时,隐藏单元有一定概率被丢弃。 3.12.1 方法 3.13.2 从零开始实现 import torch import torch.nn as nn import numpy as np import sys sys.path.append("..") import d2lzh_pytorc…

springboot---request 中Parameter,Attribute区别

HttpServletRequest类既有getAttribute()方法&#xff0c;也由getParameter()方法&#xff0c;这两个方法有以下区别&#xff1a; &#xff08;1&#xff09;HttpServletRequest类有setAttribute()方法&#xff0c;而没有setParameter()方法 &#xff08;2&#xff09;当两个…

Python之令人心烦意乱的字符编码与转码

ASC-II码&#xff1a;英文1个字节&#xff08;8 byte&#xff09;&#xff0c;不支持中文&#xff1b; 高大上的中国&#xff0c;扩展出自己的gbk、gb2312、gb2318等字符编码。 由于各个国家都有自己的编码&#xff0c;于是就需要统一的编码形式用于国际流传&#xff0c;防止乱…

[pytorch、学习] - 4.1 模型构造

参考 4.1 模型构造 让我们回顾以下多重感知机的简洁实现中包含单隐藏层的多重感知机的实现方法。我们首先构造Sequential实例,然后依次添加两个全连接层。其中第一层的输出大小为256,即隐藏层单元个数是256;第二层的输出大小为10,即输出层单元个数是10. 4.1.1 继承Module类来…

springboot---基本模块详解

概述 1.基于Spring框架的“约定优先于配置&#xff08;COC&#xff09;”理念以及最佳实践之路。 2.针对日常企业应用研发各种场景的Spring-boot-starter自动配置依赖模块&#xff0c;且“开箱即用”&#xff08;约定spring-boot-starter- 作为命名前缀&#xff0c;都位于org.…

第二课 运算符(day10)

第二课 运算符(day10) 一、运算符 结果是值 算数运算 a 10 * 10 赋值运算 a a 1 a1 结果是布尔值 比较运算 a 1 > 5 逻辑运算 a 1>6 or 11 成员运算 a "蚊" in "郑建文" 二、基本数据类型 1、数值…