泛型

                                    泛型 

一.基本概念

Java数据类型划分为基本数据类型和引用类型

基本数据类型:数值型(整型:byte,short,int,long  浮点型:float,double),字符型(char)和布尔型(boolean);

引用类型:数组,类,接口

二.问题引出

1.问题:现需定义一个描述坐标的程序类Point,此坐标类有两个属性x,y。 有如下几种解释:x=10,y=20;    x=10.1,y=20.2;     x="东京70度",y="北纬70度";即x,y可能对应不同类型,自然想到使用Object。

      public class Point{
		private Object x;
		private Object y;
		
		public Object getX() {
			return x;
		}
		public void setX(Object x) {
			this.x = x;
		}
		public Object getY() {
			return y;
		}
		public void setY(Object y) {
			this.y = y;
		}	
	}

    public class GenericParadigm {

      public static void main(String[] args) {
	        Point pIn=new Point();
	        pIn.setX(10);
	        pIn.setY(20);
	        int x=(Integer)pIn.getX();
	        int y=(Integer)pIn.getY();
	        System.out.println(x+" "+y);
	        
	        
	        Point pStr=new Point();
	        pStr.setX("东京20度");
	        pStr.setY("北纬10度");
	        String xStr=(String)pStr.getX();
	        String yStr=(String)pStr.getY();
	        System.out.println(xStr+" "+yStr);
     }
	
  }
 

   注:Point类与测试类分开写,不然编译通不过。


2.输入如下代码:
    Point p=new Point();
	p.setX(10.2);
	p.setY("北纬10度");
    String xStr=(String)p.getX();
    String yStr=(String)p.getY();
    System.out.println(xStr+" "+yStr);
    编译时报错:
    Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
	at Blog.GenericParadigm.main(GenericParadigm.java:25)

    结论:向下转型是不安全的操作。

三.认识泛型及泛型的使用方式

泛型是在类定义时并不会设置类中的属性或方法中的参数的具体类型,而是在类使用时再进行定义。若想进行此类泛型操作就必须做一个类型的标记声明。 泛型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

注:
 (1)使用泛型避免了向下转型带来的安全隐患。泛型只能用于引用类型,并且泛型不能作用于static的类或 方法上(静态方法不需要创建对象就能调用--泛型的定义)。 
 非静态方法正是由于需要先有对象,进而在使用泛型的时候可以确定安全检查的限制条件。而静态方法不需要构件对象,它在调用方法是,根本没有判断参数的约束条件,虚拟机压根就不希望这种事情发生,所以在编译时就报错了。

 
 (2)在类上自定义泛型的具体数据类型,是在使用该类创建对象的时候确定的。

 (3)如果一个类在类上已经声明了自定义泛型,但是在使用该类创建对象的时候没有指定泛型的具体数据类型时,那么默认为Object类型。 

1.泛型类

(1) 格式

 访问权限 class 类名称<泛型, 泛型...>{
    属性
    方法
 }

(2)对象的创建:

类名称<具体类型> 对象名称 = new 类名称<具体类型>();

(3)示例:

   class Point<T> {
	    private T x;
	    private T y;
	
	    public T getX() {
	        return x;
	    }
	
	    public void setX(T x) {
	        this.x = x;
	    }
	
	    public T getY() {
	        return y;
	    }
	
	    public void setY(T y) {
	        this.y = y;
	    }
  }

  public class GenericDemo01 {
	
	    public static void main(String[] args) {
	
	        Point<String> p1 = new Point<String>();
	        p1.setX("经度为10");
	        p1.setY("纬度为100");
	
	        Point<Integer> p2 = new Point<Integer>();
	        p2.setX(10);
	        p2.setY(100);
	
	        System.out.println(p1.getX() + ", " + p1.getY());
	        System.out.println(p2.getX() + ", " + p2.getY());
	    }
	}
	
  // 执行结果
  经度为10, 纬度为100
  10, 100

2.构造方法中使用泛型

 class Con<T> {

    private T value;
    // 类定义中已经定义泛型T, 方法中可以直接使用, 不用加<>
    public Con(T value) {
    this.value = value;
    }
    
    public T getValue() {
    return value;
    }
    
    public void setValue(T value) {
    this.value = value;
    }
 }
    
 public class GenericDemo02 {
    
    public static void main(String[] args) {
    Con<String> c = new Con<String>("构造方法中使用泛型");
    System.out.println(c.getValue());
    }
}

3.设置多个泛型

class Gen<K, T> {

    private K key;
    private T value;

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

}

public class GenericDemo03 {

    public static void main(String[] args) {
        Gen<String, Integer> gen = new Gen<String, Integer>();
        gen.setKey("key");
        gen.setValue(10);

        System.out.println(gen.getKey() + ", " + gen.getValue());
    }
}

4.泛型接口

(1)格式:

 interface 接口名称<泛型标识>

(2)示例

	public interface IMessage<T> {

       public void print(T t);
    }

   对此接口的实现子类有两种方法:

   .在子类实现接口时继续使用泛型
       public class MessageImpl<T> implements IMessage<T>{
			@Override
			public void print(T t) {
				System.out.println(t);
			}
			public static void main(String[] args) {
				IMessage<String> msg=new MessageImpl<String>();
				msg.print("hello world");
			}
	   }
  
  .在子类实现接口时明确给出具体类型

     public class MessageImpl implements IMessage<String>{

		@Override
		public void print(String t) {
			System.out.println(t);
		}
		public static void main(String[] args) {
			IMessage<String> msg=new MessageImpl();
			msg.print("hello world");
		}
     }      

5.泛型方法

(1)格式:

 访问权限 <泛型标识> 泛型标识 方法名称([泛型标识 参数名称])

(2)示例

public class GenericDemo06 {
	
	  public static void main(String[] args) {
		        String str = tell("Hello");
		        System.out.println(str);
		        int i = tell(10);
		        System.out.println(i);
	 }
		
	 // <T>是第一泛型参数, 写在访问权限和static后面
	 public static <T> T tell(T t) {
	        return t;
	 }

}

6.泛型数组(不建议使用)

在使用泛型方法的时候, 也可以传递或返回一个泛型数组.

  public class GenericDemo07 {

	public static void main(String[] args) {
	
	String arrStr[] = { "A", "B", "C" };
	tell(arrStr);
	
	Integer arrInt[] = { 1, 2, 3 };
	tell(arrInt);
	}

	public static <T> void tell(T arr[]) {
	for (int i = 0; i < arr.length; i++) {
	System.out.println(arr[i]);
	}
	}
 }

 注:泛型类最好不要同数组一块使用。你只能创建 new List<?>[10] 这样的数组,无法创建 new List<String>[10] 这样的。

四.泛型的通配符

在使用泛型后避免了ClassCastException问题,但使用泛型又会引出参数统一问题。

(1)示例:

示例

(2)想到函数重载:

示例

图中函数重载为什么出错??

泛型的类型擦除

Java 中的泛型和 C++ 中的模板有一个很大的不同:

C++ 中模板的实例化会为每一种类型都产生一套不同的代码,这就是所谓的代码膨胀。
Java 中并不会产生这个问题。虚拟机中并没有泛型类型对象,所有的对象都是普通类。

在 Java 中,泛型是 Java 编译器的概念,用泛型编写的 Java 程序和普通的 Java 程序基本相同,只是多了一些参数化的类型同时少了一些类型转换。

实际上泛型程序也是首先被转化成一般的、不带泛型的 Java 程序后再进行处理的,编译器自动完成了从 Generic Java 到普通 Java 的翻译,Java 虚拟机运行时对泛型基本一无所知。

当编译器对带有泛型的java代码进行编译时,它会去执行类型检查和类型推断,然后生成普通的不带泛型的字节码,这种普通的字节码可以被一般的 Java 虚拟机接收并执行,这在就叫做 类型擦除(type erasure)。 

 摘自[https://blog.youkuaiyun.com/u011240877/article/details/53545041](https://blog.youkuaiyun.com/u011240877/article/details/53545041 "泛型的类型擦除")    

泛型类并没有自己独有的 Class 类对象。比如并不存在 List<String>.class 或是 List<Integer>.class,而只有 List.class。
静态变量是被泛型类的所有实例所共享的。对于声明为 MyClass<T> 的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过 new MyClass<String> 还是 new MyClass<Integer> 创建的对象,都是共享一个静态变量。
泛型的类型参数不能用在 Java 异常处理的 catch 语句中。因为异常处理是由 JVM 在运行时刻来进行的。由于类型信息被擦除,JVM 是无法区分两个异常类型 MyException<String> 和 MyException<Integer> 的。对于 JVM 来说,它们都是 MyException 类型的。也就无法执行与异常对应的 catch 语句。

摘自[https://www.infoq.cn/article/cf-java-generics](https://www.infoq.cn/article/cf-java-generics "泛型的类型擦除")

(3)不设置泛型

 class Message<T>{
		private T note;
		public T getNote(){
			return note;
		}
		public void setNote(T note) {
			this.note = note;
		}
 }
 public class TestDemo {
   public static void main(String[] args) {
	Message<Integer> msg=new Message<Integer>();
	msg.setNote(99);
	fun(msg);
   }
   public static void fun(Message temp){//没有设置泛型类型,默认使用类型是Object
	   temp.setNote(100);
	   System.out.println(temp.getNote());
   }  
}

不设置泛型时,用户通过temp.setNote(100);可任意修改年龄。通配符可解决此问题

(4)通配符 — ?

通配符

(5)泛型上限 — ? extends 类名

格式: ? extends Number 表示只能设置Nuber或其子类

示例:

泛型上限

示例:

泛型上限错

(6)泛型下限 — ? super 类名

格式: ? super String 表示只能设置String或其父类Object

示例:

泛型下限

参考文章链接:https://mp.weixin.qq.com/s/nzPbPy1IwASBMuvK3JppjA

https://blog.youkuaiyun.com/u011240877/article/details/53545041

https://www.infoq.cn/article/cf-java-generics

https://blog.youkuaiyun.com/qq_34944851/article/details/55049106

第一次写博客,勿喷。望多多指教。各位有什么写博客的好软件可以推荐推荐,谢谢。

如有侵权,请联系立即删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值