Java 如何将方法重构为只接受类型子集的泛型方法?

Java 如何将方法重构为只接受类型子集的泛型方法?,java,generics,refactoring,Java,Generics,Refactoring,在成功地编写了一个能够完成它应该做的事情的方法(下面的示例缩短了很多)之后,我想重构它,使其不限于返回一个MyEntity的List,而是一个List。因此,它应该只能接受那些扩展myMyParentEntity的类型,但可以指定另一种类型(List,List等) 缩略示例: public static List<MyEntity> getFavList(Context context) { String prefKey = buildKey( new MyEnt

在成功地编写了一个能够完成它应该做的事情的方法(下面的示例缩短了很多)之后,我想重构它,使其不限于返回一个
MyEntity
List
,而是一个
List
。因此,它应该只能接受那些扩展my
MyParentEntity
的类型,但可以指定另一种类型(
List
List
等)

缩略示例:

 public static List<MyEntity> getFavList(Context context) {

        String prefKey = buildKey( new MyEntity() );
        List<MyEntity> entityList = new ArrayList<MyEntity>();

        SharedPreferences settings = context.getSharedPreferences(prefKey, 0);

        GSON gson = new GSON();
        MyEntity entity = gson.fromJson(settings.getString(0, null), MyEntity.class);

        entityList.add(entity);

        return entityList;
    }
公共静态列表getFavList(上下文){
String prefKey=buildKey(new MyEntity());
List entityList=新建ArrayList();
SharedReferences设置=context.getSharedReferences(prefKey,0);
GSON GSON=新的GSON();
MyEntity entity=gson.fromJson(settings.getString(0,null),MyEntity.class);
entityList.add(实体);
返回实体列表;
}

我在谷歌上搜索了很多,但我没有找到正确的方法,使这项工作没有任何编译器错误。我非常感谢任何指向解决方案的指针。

正如@keyboardsurfer已经指出的,泛型信息在运行时不可用,因此您还应该向方法提供具体的类,以便能够实例化它(您无论如何都需要它才能在
gson.fromJson()
中使用它)

要执行此操作,只需传递
T的
Class
,这样您就可以引用
T.Class
并创建
newt()
。以下问题包含更多详细信息:

例如(根据需要为
newInstance()
添加
try/catch
子句):

公共静态列表getFavList(上下文,类clazz){
字符串prefKey=buildKey(clazz.newInstance());
List entityList=新建ArrayList();
SharedReferences设置=context.getSharedReferences(prefKey,0);
GSON GSON=新的GSON();
T entity=gson.fromJson(settings.getString(0,null),clazz);
entityList.add(实体);
返回实体列表;
}
编译器将接受

List<MyEntity> list = getFavList(ctx, MyEntity.class);
List<MyAwesomeEntity> list = getFavList(ctx, MyAwesomeEntity.class);
List List=getFavList(ctx,MyEntity.class);
List=getFavList(ctx,MyAwesomeEntity.class);

关于
MyParentEntity
的具体实现的信息必须从方法参数中获取(不能从返回类型-无意义推断)。因此,您必须修改参数

我会尝试:

public static <T extends MyParentEntity> List<T> getFavList(Context context, Class<T> clazz) {

        String prefKey = buildKey( clazz.newInstance() );
        List<T> entityList = new ArrayList<T>();

        SharedPreferences settings = context.getSharedPreferences(prefKey, 0);

        GSON gson = new GSON();
        MyEntity entity = gson.fromJson(settings.getString(0, null), clazz);

        entityList.add(entity);

        return entityList;
    }
公共静态列表getFavList(上下文,类clazz){
字符串prefKey=buildKey(clazz.newInstance());
List entityList=新建ArrayList();
SharedReferences设置=context.getSharedReferences(prefKey,0);
GSON GSON=新的GSON();
MyEntity entity=gson.fromJson(settings.getString(0,null),clazz);
entityList.add(实体);
返回实体列表;
}

您需要传入
类和要传递给
buildKey
的对象

public static <T extend MyParentEntity> List<T> getFavList(
    Context context, Class<T> clazz, T key
) {

    String prefKey = buildKey(key);
    List<T> entityList = new ArrayList<T>();

    SharedPreferences settings = context.getSharedPreferences(prefKey, 0);

    GSON gson = new GSON();
    MyEntity entity = gson.fromJson(settings.getString(0, null), clazz);

    entityList.add(entity);

    return entityList;
}
公共静态列表getFavList(
上下文,类clazz,T键
) {
字符串prefKey=buildKey(key);
List entityList=新建ArrayList();
SharedReferences设置=context.getSharedReferences(prefKey,0);
GSON GSON=新的GSON();
MyEntity entity=gson.fromJson(settings.getString(0,null),clazz);
entityList.add(实体);
返回实体列表;
}
(我不知道
buildKey
应该是什么。)

由于Java具有类型擦除功能(请在此处详细阅读),您将无法轻松地获得给定给
列表的类型参数

在Java中有两个地方可以欺骗类型擦除。一种是通过创建已参数化的对象的子类。Guava's和GSON都使用这种技巧
TypeToken

请参见下面的示例:

List<MyThing> l = gson.fromJson("[{}, {}]", new TypeToken<List<MyThing>>(){}.getType());
List l=gson.fromJson(“[{},{}]”,new-TypeToken(){}.getType());
请注意,您在这里创建的是一个匿名类,它来自一个参数化的TypeToken——正如我前面解释的,这允许我们在运行时保持
列表
类型

更多信息请参见Gson文档: 或在StackOverflow上,此处:

PS:我知道您只想返回列表中的一个元素(现在?)。。。?我的答案是从JSON中拆封这些元素列表——我想你下一步会这么做?;-)如果您只想返回“列表中的单个元素”,请使用
Collections.singletonList()
或Guava的
ImmutableList.of()
,因为它们是不可变的(一件好事),并且比“普通列表”的内存占用小得多

干杯,
康拉德

List@SoboLAN问题是,这不足以替换我的类在方法中发生的所有事件。但这是正确的开始。我不明白buildkey的意思?您不能使用类而不是实例吗?@alosdev buildKey使用实例,因为它是一个快速解决方案。很抱歉造成这么多的悲痛。我们的想法是不使用名称作为键来允许重构。不能从
key.class
中推断
clazz
而不是将其作为参数来接收吗?@XaviLópez没有理由认为它应该是同一个类,而且通常有很多理由不具有该限制。+1'd。不仅仅是因为这个。假设所有键的默认构造函数可能有点大胆(可能不是getFavList的责任)·@TomHawtin tackline我使用了您的解决方案,并将其调整为完整版本。在这个过程中,我去掉了关键的论点,并在T的课堂上把它改成了可靠的。谢谢你和其他人的帮助。我不打算编译<代码>类。newInstance(总是一个糟糕的选择)将抛出各种各样的废话。@Tom同意。也不太可能所有的
T
都使用默认构造函数构建。接收实例会安全得多。请您解释一下为什么这个答案没有用处或误导性?实际上不会编译。Cla
List<MyThing> l = gson.fromJson("[{}, {}]", new TypeToken<List<MyThing>>(){}.getType());