Android、Java泛型扫盲

本文深入浅出地介绍了Java泛型的基础知识,包括无边界通配符、上下边界符的概念及应用,类型擦除的特点,以及如何利用ParameterizedType实现复杂的泛型传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们定义A、B、C、D四个类,他们的关系如下

class A {}
class B extends A {}
class C extends B {}
class D extends C {}

不指明泛型类型

//以下代码均编译通过
List list = new ArrayList();
//不指明泛型类型,泛型默认为Object类型,故能往里面添加任意实例对象
list.add(new A());
list.add(new B());
list.add(new C());
//取出则默认为Object类型
Object o = list.get(0);

这个好理解,因为所有的类都继承与Object,故能往list里面添加任意实例对象

无边界通配符

首先我们要明白一个概念,通配符意义就是它是一个未知的符号,可以是代表任意的类。

//我们发现,这样写编译不通过,原因很简单,泛型不匹配,虽然B继承A
List<A> listA = new ArrayList<B>(); 

//以下5行代码均编译通过
List<?> list;
list = new ArrayList<A>();
list = new ArrayList<B>();
list = new ArrayList<C>();
list = new ArrayList<D>();

Object o = list.get(0); //编译通过
list.add(new A());      //编译不通过
list.add(new B());      //编译不通过
list.add(new C());      //编译不通过
list.add(new D());      //编译不通过

知识点

  • 无边界通配符 能取不能存。这个好理解,因为编译器不知道?具体是啥类型,故不能存;但是任意类型都继承于Object,故能取,但取出默认为Object对象。

上边界符 ?extends

继续上代码

List<? extends C> listC;
listC = new ArrayList<A>(); //编译不通过
listC = new ArrayList<B>(); //编译不通过
listC = new ArrayList<C>(); //编译通过
listC = new ArrayList<D>(); //编译通过

C c = listC.get(0); //编译通过
listC.add(new C()); //编译不通过
listC.add(new D()); //编译不通过

知识点:

  1. 上边界符 ? extends 只是限定了赋值给它的实例类型(这里为赋值给listC的实例类型),且边界包括自身。
  2. 上边界符 ? extends 一样能取不能存,道理是一样的,虽然限定了上边界,但编译器依然不知道 ? 是啥类型,故不能存;但是限定了上边界,故取出来的对象类型默认为上边界的类型

下边界符 ?super

List<? super B> listB;
listB = new ArrayList<A>(); //编译通过
listB = new ArrayList<B>(); //编译通过
listB = new ArrayList<C>(); //编译不通过
listB = new ArrayList<D>(); //编译不通过

Object o = listB.get(0); //编译通过
listB.add(new A()); //编译不通过
listB.add(new B()); //编译通过
listB.add(new C()); //编译通过
listB.add(new D()); //编译通过

知识点

  1. 下边界符 ?super,跟上边界符一样,只是限定了赋值给它的实例类型,也包括边界自身
  2. 下边界符 ?super 能存能取,因为设定了下边界,故我们能存下边界以下的类型,当然也包括边界自身;然而取得时候编译器依然不知道 ? 具体是什么类型,故取出默认为Object类型。

类型擦除

首先我们要明白一点:Java 的泛型在编译期有效,在运行期会被删除
我们来看一段代码

//这两个方法写在同一个类里
public void list(List<A> listA) {}  
public void list(List<B> listB) {} 

上面的代码会有问题吗?显然是有的,编译器报错,提示如下信息:
list(List<A>) clashed with list(List<B>) ; both methods have same erasure
翻译过来就是,在类型擦除后,两个方法具有相同的签名,我们来看看类型擦除后是什么样子

public void list(List listA) {}  
public void list(List listB) {} 

可以看出,两个方法签名完全一致,故编译不通过。
明白了类型擦除,我们还需要明白一个概念

  • 泛型类并没有自己独有的Class类对象

比如并不存在List<A>.class或是List<B>.class,而只有List.class
接下来这个案例就好理解了

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
System.out.println(listA.getClass() == listB.getClass());  //输出true

泛型传递

现实开发中,我们经常会用到泛型传递,例如我们经常需要对Http请求返回的结果做反序列化操作

public static <T> T fromJson(String result, Class<T> type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}

此时我们传进去是什么类型,就会返回自动该类型的对象

String result="xxx";
A a = fromJson(result, A.class);
B b = fromJson(result, B.class);
C c = fromJson(result, C.class);
D d = fromJson(result, D.class);
Integer integer = fromJson(result, Integer.class);
String str = fromJson(result, String.class);
Boolean boo = fromJson(result, Boolean.class);

那如果我们想返回一个集合呢,如List<A>,下面这样明显是不对的。

//编译报错,前面类型擦除时,我们讲过,不存List<A>.class这种类型
ArrayList<A> list = fromJson(result, ArrayList<A>.class)

那我们该怎么做呢?首先,我们对fromJson改造一下,如下:

//type为一个数组类型
public static <T> List<T> fromJson(String result, Class<T[]> type) {
    try {
        T[] arr = new Gson().fromJson(result, type);//首先拿到数组
        return Arrays.asList(arr); //数组转集合
    } catch (Exception ignore) {
        return null;
    }
}

这个时候我们就可以这么做了

String result="xxx";
List<A> listA = fromJson(result, A[].class);
List<B> listB = fromJson(result, B[].class);
List<C> listC = fromJson(result, C[].class);
List<D> listD = fromJson(result, D[].class);
List<Integer> listInt = fromJson(result, Integer[].class);
List<String> listStr = fromJson(result, String[].class);
List<Boolean> listBoo = fromJson(result, Boolean[].class);

ok,我在再来,相信大多数Http接口返回的数据格式是这样的:

public class Response<T> {
    private T data;
    private int code;
    private String msg;
    //省略get/set方法
}

那这种我们又该如何传递呢?显然用前面的两个fromJson方法都行不通,我们再来改造一下,如下:

//这里我们直接传递一个Type类型
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}

这个Type是什么鬼?点进去看看

public interface Type {
    default String getTypeName() {
        return toString();
    }
}

哦,原来就是一个接口,并且只有一个方法,我们再来看看它的实现类
在这里插入图片描述
发现有5个实现类,其中4个是接口,另外一个是Class类,我们再来看看Class类的声明

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
                              //省略内部代码
}

现在有没有明白点,现在我们重点来关注下Type接口的其中一个实现接口ParameterizedType,我们来看下它的内部代码,里面就只有3个方法

public interface ParameterizedType extends Type {
    /**
     * 例如:
     * List<String> list; 则返回 {String.class}
     * Map<String,Long> map; 则返回 {String.class,Long.class}
     * Map.Entry<String,Long> entry; 则返回 {String.class,Long.class}
     *
     * @return 以数组的形式返回所有的泛型类型
     */
    Type[] getActualTypeArguments();

    /**
     * 例如:
     * List<String> list; 则返回 List.class
     * Map<String,Long> map; 则返回 Map.class
     * Map.Entry<String,Long> entry; 则返回 Entry.class
     *
     * @return 返回泛型类的真实类型
     */
    Type getRawType();

    /**
     * 例如:
     * List<String> list; 则返回 null
     * Map<String,Long> map; 则返回 null
     * Map.Entry<String,Long> entry; 则返回 Map.class
     *
     * @return 返回泛型类持有者的类型,这里可以简单理解为返回外部类的类型,如果没有外部类,则返回null
     */
    Type getOwnerType();
}

顾名思义,ParameterizedType 代表一个参数化类型。

这个时候我们来自定义一个类,并实现ParameterizedType接口,如下:

public class ParameterizedTypeImpl implements ParameterizedType {

    private Type rawType;//真实类型
    private Type actualType;//泛型类型

    public ResponseType(Type rawType,Type actualType) {
        this.rawType = rawType;
        this.actualType = actualType;
    }

    public Type[] getActualTypeArguments() {
        return new Type[]{actualType};
    }

    public Type getRawType() {
        return rawType;
    }

    public Type getOwnerType() {
        return null;
    }
}

我们再次贴出fromJson方法

//这里我们直接传递一个Type类型
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}

此时我们想得到Response<T>对象,就可以这样写

Response<A> responseA = fromJson(result, new ParameterizedTypeImpl(Response.class, A.class));
Response<B> responseB = fromJson(result, new ParameterizedTypeImpl(Response.class, B.class));
Response<C> responseC = fromJson(result, new ParameterizedTypeImpl(Response.class, C.class));

想得到List<T>对象,也可以通过ParameterizedTypeImpl得到,如下:

List<A> listA = fromJson(result, new ParameterizedTypeImpl(List.class, A.class));
List<B> listB = fromJson(result, new ParameterizedTypeImpl(List.class, B.class));
List<C> listC = fromJson(result, new ParameterizedTypeImpl(List.class, C.class));

然而,如果我们想得到Response<List<T>>对象,又该如何得到呢?
ParameterizedTypeImpl一样能够实现,如下:

//第一步,创建List<T>对象对应的Type类型
Type listAType = new ParameterizedTypeImpl(List.class, A.class);
Type listBType = new ParameterizedTypeImpl(List.class, B.class);
Type listCType = new ParameterizedTypeImpl(List.class, C.class);

//第二步,创建Response<List<T>>对象对应的Type类型
Type responseListAType = new ParameterizedTypeImpl(Response.class, listAType);
Type responseListBType = new ParameterizedTypeImpl(Response.class, listBType);
Type responseListCType = new ParameterizedTypeImpl(Response.class, listCType);

//第三步,通过Type对象,获取对应的Response<List<T>>对象
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);

然后,能不能再简单一点呢?可以,我们对ParameterizedTypeImpl改造一下

/**
 * User: ljx
 * Date: 2018/10/23
 * Time: 09:36
 */
public class ParameterizedTypeImpl implements ParameterizedType {

    private final Type   rawType;
    private final Type   ownerType;
    private final Type[] actualTypeArguments;

    //适用于单个泛型参数的类
    public ParameterizedTypeImpl(Type rawType, Type actualType) {
        this(null, rawType, actualType);
    }

    //适用于多个泛型参数的类
    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... actualTypeArguments) {
        this.rawType = rawType;
        this.ownerType = ownerType;
        this.actualTypeArguments = actualTypeArguments;
    }

    /**
     * 本方法仅使用于单个泛型参数的类
     * 根据types数组,确定具体的泛型类型
     * List<List<String>>  对应  get(List.class, List.class, String.class)
     *
     * @param types Type数组
     * @return ParameterizedTypeImpl
     */
    public static ParameterizedTypeImpl get(@NonNull Type rawType, @NonNull Type... types) {
        final int length = types.length;
        if (length > 1) {
            Type parameterizedType = new ParameterizedTypeImpl(types[length - 2], types[length - 1]);
            Type[] newTypes = Arrays.copyOf(types, length - 1);
            newTypes[newTypes.length - 1] = parameterizedType;
            return get(rawType, newTypes);
        }
        return new ParameterizedTypeImpl(rawType, types[0]);
    }

    //适用于多个泛型参数的类
    public static ParameterizedTypeImpl getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments) {
        return new ParameterizedTypeImpl(null, rawType, actualTypeArguments);
    }

    public final Type[] getActualTypeArguments() {
        return actualTypeArguments;
    }

    public final Type getOwnerType() {
        return ownerType;
    }

    public final Type getRawType() {
        return rawType;
    }

}

此时,我们就可以这样写

//第一步,直接创建Response<List<T>>对象对应的Type类型
Type responseListAType = ParameterizedTypeImpl.get(Response.class, List.class, A.class);
Type responseListBType = ParameterizedTypeImpl.get(Response.class, List.class, B.class)
Type responseListCType = ParameterizedTypeImpl.get(Response.class, List.class, C.class)

//第二步,通过Type对象,获取对应的Response<List<T>>对象
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);

现实开发中,我们还可能遇到这样的数据结构

{
    "code": 0,
    "msg": "",
    "data": {
        "totalPage": 0,
        "list": []
    }
}

此时,Response<T> 里面的泛型传List肯定是不能正常解析的,我们需要再定一个类

public class PageList<T>{
   private int totalPage;
   private List<T> list;
   //省略get/set方法
}

此时就可以这样解析数据

//第一步,直接创建Response<PageList<T>>对象对应的Type类型
Type responsePageListAType = ParameterizedTypeImpl.get(Response.class, PageList.class, A.class);
Type responsePageListBType = ParameterizedTypeImpl.get(Response.class, PageList.class, B.class)
Type responsePageListCType = ParameterizedTypeImpl.get(Response.class, PageList.class, C.class)

//第二步,通过Type对象,获取对应的Response<PageList<T>>对象
Response<PageList<A>> responsePageListA = fromJson(result, responsePageListAType);
Response<PageList<B>> responsePageListB = fromJson(result, responsePageListBType);
Response<PageList<C>> responsePageListC = fromJson(result, responsePageListCType);

注:ParameterizedTypeImpl get(Type... types)仅仅适用于单个泛型参数的时候,如Map等,有两个泛型参数以上的不要用此方法获取Type类型。如果需要获取Map等两个泛型参数以上的Type类型。可调用getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments)构造方法获取,如:

//获取 Map<String,String> 对应的Type类型
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, String.classs, String.class)

//获取 Map<A,B> 对应的Type类型
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, A.classs, B.class)

到这,泛型相关知识点讲解完毕,如有疑问,请留言。

感兴趣的同学,可以查看我的另一片文章RxHttp 一条链发送请求,新一代Http请求神器(一))里面就用到了ParameterizedTypeImpl类进行泛型传递。

<think>嗯,用户想了解Android Java的用法和概念,还有示例。首先,我需要整理的基本概念,为什么需要,然后结合Android开发中的实际应用场景。用户提供的引用内容里提到是从JDK 1.5引入的,类型擦除机制,以及通配符的使用,比如Producer-Extends原则。需要确保回答覆盖这些点,同时给出具体的例子,比如在Android中常见的场景,比如RecyclerView.Adapter中使用来处理不同数据类型的ViewHolder。 首先,的作用是什么?类型安全,避免强制类型转换,提高代码复用。比如在Android中,很多容器类如List、Map都使用。例如,在创建Adapter时,通常会定义类型来指定数据模,这样在onBindViewHolder方法中就可以直接使用具体类型,不需要强制转换。 然后,类型擦除的问题。在编译时会进行类型擦除,替换为Object或边界类型。比如引用[2]提到类型擦除是实现的机制,所以在运行时无法获取的具体类型信息。比如在Android中,使用Gson解析JSON时,需要传递TypeToken来保留信息,因为直接使用List<String>.class是不允许的,这时候TypeToken利用了匿名内部类来保存类型参数。 接下来,通配符的使用。在Android开发中,处理集合时可能会用到通配符,比如List<? extends View>表示可以是任何View的子类,这样方法可以接受不同类型的View集合。而引用[5]中的sumOfNumbers方法使用了extends通配符,类似地,在Android中处理数据时,如果方法需要从集合中读取数据,可以使用extends,如果需要写入,则使用super,遵循PECS原则(Producer-Extends, Consumer-Super)。 还需要提到Android中的实际应用,比如BaseAdapter或RecyclerView.Adapter的封装。例如,自定义一个BaseRecyclerAdapter,使用来指定数据类型和ViewHolder,这样在onBindViewHolder中可以直接操作具体的数据类型,提高代码的可读性和安全性。 此外,需要注意的限制,比如不能实例化类型的对象,不能用于静态变量等。例如,在Android中,如果尝试创建T的实例会编译错误,因为类型擦除后T实际上是Object,无法确定具体类型。这时候可以通过传递Class<T>参数或者使用反射来解决,但需要谨慎处理。 最后,给出示例代码,比如类的定义,方法的使用,以及在RecyclerView中的应用示例。确保示例Android开发的实际场景,帮助用户理解如何在自己的项目中应用。 需要检查引用是否正确,比如引用[4]提到instanceof检查类型会报错,这是因为类型擦除后无法在运行时判断的具体类型,所以在代码中要避免这样的操作,或者通过其他方式绕过这个限制。 总结回答的结构:首先介绍的作用,然后详细讲解核心概念,包括类型擦除通配符方法等,接着给出在Android中的使用示例,最后总结注意事项。确保每个部分都有对应的引用支持,并且示例代码正确展示用法。</think>### Android Java 使用详解 #### 一、的作用与核心概念 通过参数化类型实现代码复用,增强类型安全性,消除强制类型转换。例如,在 Android 开发中,使用可以避免因数据类型错误导致的运行时异常[^1]。其核心机制是**类型擦除**:编译时类型会被擦除边界类型(如 `Object` 或指定上限类型),运行时无法直接获取的具体类型信息[^2]。 #### 二、Android 中的典用法 ##### 1. 类定义 ```java public class Response<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } // 使用示例 Response<User> userResponse = new Response<>(); userResponse.setData(new User("Android")); User user = userResponse.getData(); // 无需强制转换 ``` ##### 2. 方法 ```java public <T> T fromJson(String json, Class<T> clazz) { return new Gson().fromJson(json, clazz); } // 使用示例 User user = fromJson(jsonString, User.class); ``` ##### 3. 通配符与 PECS 原则 - **`<? extends T>`**(生产者模式):用于读取数据 ```java void processViews(List<? extends View> views) { for (View view : views) { view.setVisibility(View.VISIBLE); } } ``` - **`<? super T>`**(消费者模式):用于写入数据 ```java void addNumbers(List<? super Integer> list) { list.add(42); } ``` #### 三、Android 中的实际应用示例 ##### RecyclerView 封装 ```java public abstract class BaseAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { private List<T> dataList; public void updateData(List<T> newData) { dataList = newData; notifyDataSetChanged(); } @Override public void onBindViewHolder(@NonNull VH holder, int position) { T item = dataList.get(position); bindView(holder, item); // 抽象方法处理具体绑定逻辑 } protected abstract void bindView(VH holder, T item); } // 具体实现 public class UserAdapter extends BaseAdapter<User, UserViewHolder> { @Override protected void bindView(UserViewHolder holder, User user) { holder.nameView.setText(user.getName()); // 直接访问 User 类型的属性,无需类型检查 } } ``` #### 四、注意事项 1. **类型擦除限制** - 运行时无法使用 `instanceof` 判断类型(如 `if (obj instanceof List<String>)` 会编译报错)[^4] - 类中的静态变量不能使用类型 2. **类型擦除解决方案** ```java // 通过传递 Class 对象保留类型信息 public class ApiClient<T> { private Class<T> type; public ApiClient(Class<T> type) { this.type = type; } public T parseResponse(String json) { return new Gson().fromJson(json, type); } } ``` 3. **PECS 原则应用** 在 Android 数据流处理中,应根据场景选择通配符: ```java // 生产者场景(读取数据) void logList(List<? extends Serializable> list) { for (Serializable item : list) { Log.d("TAG", item.toString()); } } // 消费者场景(写入数据) void fillList(List<? super Number> list) { list.add(1); list.add(3.14); } ``` #### 五、总结 Android 开发中广应用于数据封装、组件复用、类型安全校验等场景。理解类型擦除机制和通配符规则,能有效避免 `ClassCastException` 并提升代码健壮性[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值