java泛型机制(沫言优拓)笔记
- 泛型问题的引出
设计一个类实现发消息,但可以不同数据类型的消息,要求可以接收所有的数据类型
用object是所有的类的父类进行接收
程序本来要发送一个double类型的消息,但由于开发者的不严谨,将object类型强制转化成String类型,而导致程序出现ClassCastException异常,而且该异常在编译期间无法发现,在运行期间将会出现,这会让我们的程序产生很大的安全隐患
由此引出泛型的机制来解决这个问题(jdk1.5)
- 泛型的基本定义
泛型的特点:在声明一个变量或者属性的时候不设置其数据类型,而是在指定结构使用的时候进行动态的配置。使用泛型解决了强制类型转化的设计缺陷
如果设置了泛型,这个泛型将成为setter方法的形参的类型,getter方法的返回值类型,如果此时设置的数据有误,编译器会在编译期间进行检查,并报告错误,并且数据是直接以Integer都其他类型进行储存的,避免了强制类型的转化带来的ClassCastException异常。从重根本上解决了这个问题
class Massage<T>{
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(this.content);
}
}
public class Fanxing {
public static void main(String[]args){
Massage<Double> ma=new Massage<Double>();
ma.setContent(34);
}
}
使用泛型过程中的注意点
-
设置泛型的类型的过程中只允许使用类,所有的基本数据类型必须设置成他们对应的包装类
-
JDK1.7时对泛型的定义做出了简化:
Massagerma=new Massage<>(); -
如果使用一个拥有泛型的类的时候,在使用的使用没有指定其泛型直接使用,为了保证程序不出错,会用Object作为默认的泛型类型
//验证泛型的使用
class Massage<T>{
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void main(String[]args){
Massage ma=new Massage();
ma.setContent(34);
int a=(Integer)ma.getContent();
System.out.println(a);//34
//注: Fanxing.java使用了未经检查或不安全的操作。
//注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
}
}
此时在编译 期间会有警告出现,在开发环境比较严格的情况下,出现警告信息的程序不允许发布
- 泛型通配符--实现对象的引用传递
class Massage<T>{
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage<String>a){
a.sentMassage();
}
public static void main(String[]args){
Massage<String>ma=new Massage<>();
ma.setContent("haha");
info(ma);//haha
}
}
此时可以完成引用传递
**但当将泛型的类型换成
class Massage<T>{
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage<String> a){
a.sentMassage();
}
public static void main(String[]args){
Massage<Integer>ma=new Massage<>();
ma.setContent(34);
info(ma);//haha
}
}
java:33: 错误: 不兼容的类型: Massage
Integer类型无法向String类型进行转化,而导致编译错误
- 可能的解决方案--对调用方法进行重载
-
public class Fanxing {
public static void info(Massage
a.sentMassage();
}
public static void info(Massage
a.sentMassage();
}
public static void main(String[]args){
Massage<Integer>ma=new Massage<>();
ma.setContent(34);
info(ma);//haha
}
}
但是还是失败了,java:28: 错误: 名称冲突: info(Massage<Integer>)和info(Massage<String>)具有相同疑符
**即使泛型的类型不同,但是基本的类的类型相同,这种情况下不能进行重载**
- 另外一种可能的途径
//验证泛型的使用
class Massage
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage a){
a.sentMassage();
}
public static void main(String[]args){
Massage<String>ma=new Massage<>();
ma.setContent("哈哈哈");
info(ma);//哈哈哈
}
}
**在接收的函数中没有设置类的泛型类型,将默认泛型类型为Object.可以发现可以进行引出传递**
但是这样会造成安全问题:
//验证泛型的使用
class Massage
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage a){
a.setContent(34);//此时可以将原来设置的数据修改成任意类型的数据
a.sentMassage();
}
public static void main(String[]args){
Massage<String>ma=new Massage<>();
ma.setContent("哈哈哈");
info(ma);
}
}
注: Fanxing.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
**如果此时都要Object进行接收,那么我们所设置的泛型就没有发挥作用,使用object作为默认的泛型类型,确实可以做到引用传递,但是也是因为object,我们所设置的数据可以被修改成任意类型的数据,从而造成看数据的不安全**
**我们给出对象传递的要求:**
**可以接受该类型的所有泛型的对象,而且要求不允许对内容进行修改**
由此java给出了通配符"?"的概念,用?作为函数的泛型类型可以解决这个问题:
//验证泛型的使用
class Massage
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage<?> a){
a.setContent(34);//此时可以将原来设置的数据修改成任意类型的数据
a.sentMassage();
}
public static void main(String[]args){
Massage<String>ma=new Massage<>();
ma.setContent("哈哈哈");
info(ma);
}
}
如果修改数据将会在编译不通过:
ng.java:28: 错误: 不兼容的类型: int无法转换为CAP#1
由此引出了通配符的2个子通配符
- 上限的使用
//验证泛型的使用
class Massage<T extends Number>{//该类的泛型类型只能是Number类和Number类的子类
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage<? extends Number> a){//此时只能接受该类型的Number类和Number的子类的泛型对象
a.sentMassage();
}
public static void main(String[]args){
Massage<Double>ma=new Massage<>();
ma.setContent(34.3);
info(ma);
}
}
如果使用Massage对象进行处理的使用,所给出Massage类的泛型类型不是Number或是其对象的时候,程序将会出现语法错误
- 下限的使用
//验证泛型的使用
class Massage<T>{//该类的泛型类型只能是Number类和Number类的子类
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage<? super String> a){//表明该函数只能接受String类或者其父类
a.sentMassage();
}
public static void main(String[]args){
Massage<Double>ma=new Massage<Double>();
ma.setContent(23.5);
info(ma);
}
}
Fanxing.java:36: 错误: 不兼容的类型: Massage<Double>无法转换为Massage<? super String>
此时要进行传递的是该类型的Double型的对象,Double类不是不是String类或者是其父类,将会在编译期间出现错误
通过对这种上限和下限的处理可以使得在进行泛型参数接受的的操作中更加方便实现数据类型的控制
注意:
//验证泛型的使用
class Massage<T extends Number>{//该类的泛型类型只能是Number类和Number类的子类
private T content;
public boolean tell(double a){
return true;
}
public void setContent(T content){
this.content=content;
}
public T getContent(){
return this.content;
}
public void sentMassage(){
System.out.println(content);
}
}
public class Fanxing {
public static void info(Massage<? super String> a){//表明该函数只能接受String类或者其父类
a.sentMassage();
}
public static void main(String[]args){
Massage<Double>ma=new Massage<Double>();
//info(ma);
}
}
该处即使没有调用info函数也会报错,因为该函数只能接受String或者其父类,但是Massage类只能产生Number类或者其子类的泛型对象,两者永远没有交际,多该函数的直接会在编译期间报错
- 泛型接口
之前的泛型都上定义在类上的,在接口中也可以进行泛型的定义,有2种定义泛型接口的方法
方法一:
//泛型接口的定义
interface IMassage<T>{
public void sent(T t);
}
class Massage<T> implements IMassage<T>{
public void sent(T t){
System.out.println("发送的消息是:"+t);//对接口的sent方法进行覆写
}
}
public class Fanxings{
public static void main(String[]args){
IMassage<String> st=new Massage<String>();//向上转型实现接口的实例化
st.sent("hhhieiei");
}
}
由于此时子类在定义的使用继续使用了泛型定义,所以在实现IMassage接口的时候才可以继续通过泛型的方法进行定义,而最终泛型接口和子类的泛型类型要统一
- 方法2(更常用)
//泛型接口的定义
interface IMassage<T>{
public void sent(T t);
}
class Massage<T> implements IMassage<String>{//固定接口的泛型类型
//后面子类就可以写具体类型
public void sent(String t){
System.out.println("发送的消息是:"+t);//对接口的sent方法进行覆写
}
}
public class Fanxings{
public static void main(String[]args){
IMassage<String> st=new Massage<String>();//向上转型实现接口的实例化
st.sent("hhhieiei");
}
}
要判断泛型接口的定义方式,看子类的定义即可
泛型方法
//泛型方法的使用
class Massage{
public <T> T sentMassage(T t){
return t;
}
}
public class Fanxings{
public static void main(String[]args){
Massage ma=new Massage();
System.out.println(ma.sentMassage(34.4));
System.out.println(ma.sentMassage("小样!"));
System.out.println(ma.sentMassage(12));
}
}
output:
34.4
小样!
12
方法中泛型的具体具体类型由传入的数据的实际类型自动决定,
- 泛型方法的实际意义
需要结合反射机制,才能发挥泛型方法的具体作用