简体   繁体   English

泛型方法的泛型返回类型

[英]Generic return type from a generic method

Consider a generic method definition like this: 考虑这样的通用方法定义:

public static <T> T getParameter(final Context context, final ContextParameter contextParameter, final Class<T> clazz) {
    return (T) context.get(contextParameter.name());
}

Works perfectly to get any type of object until you try a generic object like List<String>. 在尝试像List <String>这样的通用对象之前,可以完美地获取任何类型的对象。 The only way I could make it work was like this: 我能使它工作的唯一方法是这样的:

 final List<String> fileNames = 
             (List<String>) ContextUtils.getParameter(context,
                             ContextParameter.EOF_FILE_NAMES_TO_ZIP, List.class);

But that requires an extra cast, removing the purpose of having the method. 但这需要额外的演员阵容,从而消除了使用该方法的目的。 Is there a way to be able to provide List<String>.class or something similar to the method? 有没有办法能够提供List<String>.class或类似的方法? (I know that type erasure prevents List<String> to exist but maybe there is a way to do it). (我知道类型擦除会阻止List<String>存在,但也许有办法实现它)。

Note: With making it work I mean without having a type safety warning. 注意:使其工作我的意思是没有类型安全警告。 I'm aware that the code would work as it is but it is not type safe. 我知道代码可以正常工作,但它不是类型安全的。

You can't do this in a type safe way without modifying the downstream code ( context.get() ) because the generic type is erased at run time. 如果不修改下游代码( context.get() ),则无法以类型安全的方式执行此操作,因为泛型类型在运行时被擦除。 In fact, currently the clazz argument isn't being used at all: you could simply delete that argument and the method would perform identically since the cast to (T) is currently a no-op due to erasure. 事实上,目前clazz参数根本没有被使用:你可以简单地删除那个参数,并且该方法将执行相同的操作,因为由于擦除而转换为(T)当前是无操作。 Your current code is not type safe even for non-genric classes. 即使对于非genric类,您当前的代码也不类型安全的。

So if you don't care about the safety, and just want to avoid having casts in the client code, you can just delete the last argument, and a suppress warnings once on the method. 因此,如果您不关心安全性,并且只是想避免在客户端代码中进行强制转换,则可以删除最后一个参数,并在方法上禁止一次警告。

If you want type safety, you can use super type tokens to keep the generic type information: Guava's TypeToken works and is easily available (IMO every Java project should use Guava already). 如果你想要类型安全,你可以使用超类型令牌来保存泛型类型信息:Guava的TypeToken工作并且很容易获得(IMO每个Java项目都应该使用Guava)。

It will require downstream changes to your code, however - since you need to capture the type information when objects are added and check it when they come out. 但是,它需要对代码进行下游更改,因为您需要在添加对象时捕获类型信息,并在它们出现时进行检查。 You didn't share the code for your Context class, so it's hard to say if that's possible, but in any case you'd want something like this: 你没有共享你的Context类的代码,所以很难说这是否可能,但无论如何你想要这样的东西:

static class TypedParam {
    final TypeToken<?> type;
    final Object object;
    private TypedParam(TypeToken<?> type, Object object) {
        this.type = type;
        this.object = object;
    }
}

public static class Context {
    Map<String, TypedParam> params = new HashMap<>();
    TypedParam get(String name) {
        return params.get(name);
    }
}

public static <T> T getParameter(final Context context, final String name, final TypeToken<T> type) {
    TypedParam param = context.get(name);
    if (type.isAssignableFrom(param.type)) {
        @SuppressWarnings("unchecked")
        T ret = (T)param.object;
        return ret;
    } else {
        throw new ClassCastException(param.type + " cannot be assigned to " + type);
    }
}

Basically you know the generic type of all the objects in your parameter map, and check with a "generic aware cast" at run time. 基本上,您知道参数映射中所有对象的泛型类型,并在运行时使用“通用感知强制转换”进行检查。 I didn't include Context.put() above, but you'd need to capture the type info when the parameter was added. 我上面没有包含Context.put() ,但是在添加参数时需要捕获类型信息。

You still have a @SuppressWarnings("unchecked") , but here it's provably type-safe since you are maintaining the type information (in the guts of many generic classes you'll find provably safe casts, so they can't always be avoided even in correct code). 你仍然有一个@SuppressWarnings("unchecked") ,但是这里它可以证明是类型安全的,因为你正在维护类型信息(在许多通用类的内容中你会发现可证明安全的类型,所以它们不能总是避免即使是正确的代码)。

As you already said, there is nothing like List<String>.class . 正如您已经说过的,没有像List<String>.class So if you just want to read parameters without casts you can use something like this: 因此,如果您只想读取没有强制转换的参数,可以使用以下内容:

public static <T> T getParameter(final Map<String, Object> context, final String name) {
    return (T) context.get(name);
}

I assume the your context returns Object as well and you need to suppress type safety checks anyway for that one method. 我假设你的上下文也返回Object ,你需要为这一个方法抑制类型安全检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM