浅析Java泛型

113 阅读5分钟

1.为什么使用泛型

早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。

public static void main(String[] args) {
        //测试一下泛型的经典案例
        ArrayList arrayList = new ArrayList();
        arrayList.add("helloWorld");
        arrayList.add("taiziyenezha");
        arrayList.add(88);//由于集合没有做任何限定,任何类型都可以给其中存放
        for (int i = 0; i < arrayList.size(); i++) {
            //需求:打印每个字符串的长度,就要把对象转成String类型
            String str = (String) arrayList.get(i);
            System.out.println(str.length());
        }
    }
  • 泛型的第一作用:起到约束和规范的作用,约束类型属于某一个,规范使用只能用某一种类型。可以让我们业务变得更加清晰和明了并得到了编译时期的语法检查。
  • 泛型的第二作用:使用泛型的类型或者返回值的方法,自动进行数据类型转换。

2.什么是泛型

是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊类型,也就是说在泛型使用过程中操作的数据类型被指定为一个参数,而这种参数类型可以用在类,方法,接口中分别被称为泛型类,泛型方法,泛型接口。 类泛型化代号,在开发或者源码中我们经常可以看见A-Z定义,比如ArrayList,这个R只是一个代号,他泛指Object,相当于写了一个Object。泛型也是一个类型,在定义的时候毫无意义,我们应该把它当成Object代号,只有在使用的时候对泛型进行实例化的时候才会起作用。

3.什么情况下会封装和使用泛型

在多个类的开发中,如果出现相同的方法签名和返回值类型相同,参数类型不同,多个类中出现大量重载方法,一般根据java封装思想的话会采用父类进行定义公共的方法和属性然后让其子类继承从而起到约束和规范的作用。这种采用继承封装的方法会出现以下几种问题。 怎么定义方法的返回值和参数值类型,因为每个业务的方法返回值和参数类型是不一样的;早期的做法是使用object;

  • 使用object修饰方法和参数的时候会存在两个问题:参数接收者必须要类型转换
@Override
public int save(Object obj) {
    User user =(User) obj; //参数接受者,必须要类型转换,
    return 0;
}
@Override
public int save(Object obj) {
    Order order =(Order) obj; //参数接受者,必须要类型转换,
    return 0;
}
  • 方法调用者必须要类型转换==即方法的返回值
    public static void main(String[] args) {
    IUserService userService = new UserServiceImpl();
    User user = (User)userService.getById(1L);//法调用者,必须要类型转换
}

    public static void main(String[] args) {
        IOrderService orderService = new OrderServiceImpl();
        Order order = (Order)orderService.getById(1L); // 法调用者,必须要类型转换
    }

4.使用泛型的好处

  • 避免了类型强转的麻烦;
  • 提供了编译器的类型安全,避免了在运行时出现强转异常。

5.泛型的使用

泛型虽然通常会被大量的使用在集合当中,但是我们也可以完整的学习泛型只是。泛型有三种使用方式,分别为:泛型类、泛型方法、泛型接口。将数据类型作为参数进行传递。

5.1泛型类

泛型类用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种集合框架容器类。比如:List,Set,Map。 泛型类的定义格式:public class 类名

  • 此处的T可以随便写为任意标识,常见的有T、E等形式的参数表示泛型
  • 泛型在定义的时候不具体,使用的时候才变得具体。
  • 在使用的时候确定泛型的具体数据类型。即在创建对象的时候确定泛型。
public class GenericsClassDemo<T> {
    //t这个成员变量的类型为T,T的类型由外部指定
    private T t;
    //泛型构造方法形参t的类型也为T,T的类型由外部指定
    public GenericsClassDemo(T t) {
        this.t = t;
    }
    //泛型方法getT的返回值类型为T,T的类型由外部指定
    public T getT() {
        return t;
    }
}

5.2泛型方法
泛型方法是在调用方法的时候指明泛型的具体类型。
定义格式:public <R> 方法名 (T 参数){
}
    /**
     * @param t 传入泛型的参数
     * @param <T> 泛型的类型
     * @return T 返回值为T类型
     * 说明:
     *   1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
     *   2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
     *   3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
     *   4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E等形式的参数常用于表示泛型。
     */
    public <T> T genercMethod(T t){
        System.out.println(t.getClass());
        System.out.println(t);
        return t;
    }

调用方法时,确定泛型的类型

public static void main(String[] args) {
    GenericsClassDemo<String> genericString  = new GenericsClassDemo("helloGeneric"); //这里的泛型跟下面调用的泛型方法可以不一样。
    String str = genericString.genercMethod("hello");//传入的是String类型,返回的也是String类型
    Integer i = genericString.genercMethod(123);//传入的是Integer类型,返回的也是Integer类型
}

5.3泛型接口

泛型接口与泛型类的定义及使用基本相同,泛型接口常被用在各种类的生产容器中。 定义格式:public interface 接口名 { }

 * 定义一个泛型接口
 */
public interface GenericsInteface<T> {
    public abstract void add(T t); 
}

6.通配符

待更

7.实现案例

定义接口

public interface UService<T,R>{
//    根据id查询
    R getById(T id);
    //根据id删除
    T removeId(R id);
}

用户类

@Slf4j
@Service
public class UserServiceImpl implements UService<User,Long> {

    public Long getById(User id) {
        return null;
    }
    public User removeId(Long id) {
        return null;
    }
}

订单类

@Slf4j
@Service
public class OrderServiceImpl implements UService<Order,Long> {
    public Long getById(Order id) {
        return null;
    }
    public Order removeId(Long id) {
        return null;
    }
}