什么是java泛型
Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
为什么使用泛型
1.可以提高代码的复用性
可以将代码中的数据类型参数化,使代码适用于不同的数据类型,从而提高代码的重要性。
2.增强代码的类型安全性
通过在编译时进行类型检测可以避免在运行时出现类型转换异常等错误
创建Point类
package com.dome;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Point{
/**
* 这里使用Object是因为Object是所有类的父类
* 类会自动向上转型-----根据多态
*/
public Object x;
public Object y;
}
测试类test
package com.dome;
public class Text {
public static void main(String[] args) {
// 定义x,y都是整形
Point p1 = new Point(10, 20);
// 定义x,y都是字符串类型
Point p2 = new Point("北纬39.1度", "东经114.3度");
// 定义x,y都是小数
Point p3 = new Point(13.4, 33.3);
// 定义一个为整形一个为字符串类型
Point p4 = new Point(20, "你好");
String x2 = (String) p2.getX();
String y2 = (String) p2.getY();
// Integer x4= (Integer) p4.getX();可以正常运行但是我们这里为了测试泛型的数据类型安全性问题所以先不使用
String x4 = (String) p4.getX();
String y4 = (String) p4.getY();
}
}
运行后会报错误:Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.dome.Text.main(Text.java:16)
我们可以使用泛型来解决上面的数据类型安全问题。
定义泛型类
定义泛型类的语法
泛型类使用的注意
数据类型安全问题
package com.demo;
import com.demo.Point;
public class Text {
public static void main(String[] args) {
Point<String> p = new Point<String>("北纬30", "大多数");
String x = p.getX();
String y = p.getY();
Point<Integer> p2 = new Point<Integer>(12, 25);
Integer x2 = p2.getX();
Integer y2 = p2.getY();
}
}
泛型的应用
通配符
在开发中对象的引用传递是最常见的,但是如果在泛型类的操作中,在进行引用传递时泛型类型必须匹配才可以传递,否则是无法传递的。
package com.demo2;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Point <E>{
private E fun;
public void zg(){
System.out.println("fun的值=="+fun);
}
}
测试方法
package com.demo2;
public class Test {
public static void main(String[] args) {
Point<String> p1 = new Point<String>();
p1.setFun("你好");
Point<Integer> p2 = new Point<Integer>();
p2.setFun(23);
Point<Double> p3 = new Point<Double>();
p3.setFun(10.2);
fu(p1);
fu(p2);
fu(p3);
}
/**
* 通配符
* 使用通配符后可以接受任意的泛型类型,必须使用通配符号 ?
* ?为泛型的通配符
* @param point
*
*
*/
public static void fu(Point<?> point){
point.zg();
}
}
泛型的上限与下限
在引用传递中,可以在泛型操作中设置泛型对象的范围上限与下限。上限使用extends关键字声明,表示参数化的类型可以是指定的类型或者此类型的子类。下限使用super进行声明,表示参数化的类型可以使所指定类型或者此类型的父类。
上限与下限方法
上限方法
声明对象: 类名称<? extends 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 extends 类>{}
下限方法
声明对象: 类名称<? super 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 super 类>{}
上限与下限的使用
方法
package com.demo5;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
class Point<E> {
private E fun;
public void zg() {
System.out.println("fun=" + fun);
}
}
测试
package com.demo5;
public class Text {
public static void main(String[] args) {
Point<Integer> p=new Point<Integer>();
Point<Number> p2=new Point<Number>();
Point<String> p3=new Point<String>();
Point<Double> p4=new Point<Double>();
Point<Object> p5=new Point<Object>();
fu1(p);
fu1(p2);
fu1(p5);//Object为Number的父类
fu2(p2);
fu2(p);//Integer为Number的子类
}
//泛型上限--泛型的类型不能超过Number 要么为Number要么为Number的子类
public static void fu1(Point<? extends Number> point){
point.zg();
}
//泛型下限--泛型的类型不能小于Number 要么为Number要么为Number的父类
public static void fu2(Point<? super Number> point) {
point.zg();
}
}
泛型接口
语法
public interface 接口名<泛型标识,....>{
}
方法
接口用来被类实现。一个类如何实现泛型接口。有两种方式
package com.demo;
public class Text {
public static void main(String[] args) {
Headset h = new Headset();
h.Type("有线耳机");
Charger c = new Charger();
c.Type("华为充电器");
}
}
//定义泛型接口
interface Type<E> {
public abstract void Type(E e);
}
//第一种 类实现泛型接口时为其指定泛型类型。
class Headset implements Type<String> {
public void Type(String s) {
System.out.println("耳机:" + s);
}
}
//第二种 类实现接口时该类也设置为泛型类,标志必须和接口的泛型标志一致
class Charger<E> implements Type<E> {
public void Type(E e) {
System.out.println("充电器:" + e);
}
}
注解
预定义注解
@Override: 当方法使用该注解时,表示该方法是一个重写的方法,那么该方法必须符合重写的规则【子类重写的方法名必须和父类的方法名一致,参数也要一致,返回值也要一致 访问修饰不能小于父类的<public protected 默认 private> 抛出的异常不能大于父类】。
@Deprecated: 标记该方法已经过时。JVM
@FuncationInterface: 函数式接口.---要求接口钟有且仅有一个抽象方法
自定义注解
语法:
public @interface 注解名{
}
使用
package com.demo;
public class Test {
public static void main(String[] args) {
Hello h = new Hello();
h.show("嗨");
}
}
//自定义注解
@interface Rr {
}
@Rr
class Hello {
@Rr
private String name;
@Rr
public void show(@Rr String S) {
System.out.println("=" + S);
}
}
元注解
package com.demo4;
import java.lang.annotation.*;
//Retention限制注解什么时候生效
// 源码时生效--SOURCE,
//字节码时生效--CLASS,
//运行时生效--RUNTIME 涉及反射。
@Retention(value = RetentionPolicy.CLASS)
//Target控制该注解使用的地方
//使用在类上--TYPE,
//使用在属性上--FIELD,
//使用在方法上--METHOD,
//使用在参数--PARAMETER,
//使用在构造方法上--CONSTRUCTOR
@Target(value = {ElementType.TYPE, ElementType.FIELD})
//在生成api文档时含有注解要使用该注解。
@Documented
public @interface MyAnn {
}
自定义注解--【属性】
只传一个值
package com.demo4;
/**
* 如果只为value赋值这里的value可以省略不写
* 如果值只有一个可以省略{},多个的话必须给{}
*/
@MyAnn1("学生")
public class Student {
private String name;
public void show(String n){
System.out.println("---"+n);
}
}
注解类
package com.demo4;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
//@Target()默认的可以在任意地方使用,如果只有一个值的话value可以不写
@Target(ElementType.TYPE)
//@Retention()默认在字节码生效
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnn1 {
String value() default "";
}
传多个值
package com.demo4;
/**
* 如果只为value赋值这里的value可以省略不写
* 如果值只有一个可以省略{},多个的话必须给{}
*/
@MyAnn1(value = {"学生", "67"})
public class Student {
private String name;
public void show(String n){
System.out.println("---"+n);
}
}
注解类
package com.demo4;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
//@Target()默认的可以在任意地方使用,如果只有一个值的话value可以不写
@Target(ElementType.TYPE)
//@Retention()默认在字节码生效
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnn1 {
String[] value() default {};
int age() default 34;
}