Java泛型和枚举,模板参数丢失 [英] Java Generics and Enum, loss of template parameters

查看:49
本文介绍了Java泛型和枚举,模板参数丢失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当复杂的结构,它没有按预期工作.这就是我所做的:

I have a fairly complicated structure, and it is not working as intended. This is what I did:

public interface ResultServiceHolder {
    <M, ID extends Serializable, BO extends BusinessObject<M, ID>> ResultService<M, ID, BO> getService();
}

public enum ResultTypes implements ResultServiceHolder {
    RESULT_TYPE_ONE {
        @Override
        public ResultOneService getService() { //unchecked conversion?
            return serviceInitializer.getResultOneService();
        }
    },
    RESULT_TYPE_TWO {
        @Override
        public ResultTwoService getService() {  //unchecked conversion?
            return serviceInitializer.getResultTwoService();
        }
    },
    RESULT_TYPE_THREE {
        @Override
        public ResultThreeService getService() {  //unchecked conversion?
            return serviceInitializer.getResultThreeService();
        }
    };

    protected ServiceInitializer serviceInitializer;


    protected void setServiceInitializer(ServiceInitializer serviceInitializer) {
        this.serviceInitializer = serviceInitializer;
    }

    @Component
    public static class ServiceInitializer {
        @Autowired
        private ResultOneService resultOneService;

        @Autowired
        private ResultTwoService resultTwoService;

        @Autowired
        private ResultThreeService resultThreeService;

        @PostConstruct
        public void init() {
            for(ResultTypes resultType : ResultTypes.values()) {
                resultType.setServiceInitializer(this);
            }
        }

        //getters
    }
}

目的是概括基于枚举的调用,而是能够迭代枚举数组.

The purpose was to generalize the call based on enums, and rather, just be able to iterate on the array of enums.

    for(ResultServiceHolder resultServiceHolder : ResultTypes.values()) {
        if(resultServiceHolder.equals(post.getPostResultTypeCode())) {
            return resultServiceHolder.getService().createResultSearchCriteriaResponse(postId);
        }
    }

这工作得很好,很花哨.但是,如果我要说

And this is working fine and dandy. However, if I'd say

ResultTypes.RESULT_TYPE_ONE.getService().getRepository()

那么它是一个 BaseRepository 而不是 BaseRepository.方法 resultTypeHolder.getService() 返回了 ResultService,但最后变成了 Object可序列化.

Then it is a BaseRepository<Object, Serializable> rather than a BaseRepository<ResultTypeOne, Long>. The method resultTypeHolder.getService() gives back ResultService<M, ID, BO>, but in the end, it becomes Object andSerializable.

我做错了什么?如何保留通用参数类型?

我想补充一点,是的,我确实意识到问题出在未经检查的铸造的某个地方.但是服务被定义为

I'd like to add that yes, I do realize the problem is somewhere with the unchecked casting. But the services are defined as

public interface ResultTypeOneService
    extends ResultService<ResultTypeOne, Long, ResultTypeOneBO> {
}

而且我不知道为什么不推断类型.

And I don't know why the types are not inferred.

编辑:从技术上讲,如果我明确推断它们就可以工作:

EDIT: Technically, it works if I explicitly infer them:

ResultTypes.RESULT_TYPE_ONE.<ResultTypeOne, Long, ResultTypeOneBO>getService().getRepository()

但它应该是自动的,为什么它不自动工作?我应该为它提供某种包含类型的对象吗?为什么返回类型不够?

But it ought to be automatic, why is it not working automatically? Am I supposed to provide it with some kind of object that contains the type? Why is the return type not enough for that?

EDIT2:ResultTypeOne 的超类是

@SuppressWarnings("serial")
@EntityListeners(EntityListener.class)
@MappedSuperclass
public abstract class EntityBase implements Serializable {

但它没有映射到边界的任何地方.

But it is not mapped anywhere in the bounds.

EDIT3:非常感谢@Radiodef!理论解决方案最终如下所示,并且可以正常工作:

EDIT3: A big thank you to @Radiodef! The theoretic solution ended up to be the following, and would work perfectly fine:

public interface ResultServiceHolder<M, ID extends Serializable, BO extends BusinessObject<M, ID>> {
    ResultService<M, ID, BO> getService();
}

public abstract class ResultTypes<M, ID extends Serializable, BO extends BusinessObject<M, ID>>
    implements ResultServiceHolder<M, ID, BO> {

    public static ResultTypes<?, ?, ?>[] values() {
        return new ResultTypes<?, ?, ?>[] {RESULT_ONE, RESULT_TWO, RESULT_THREE};
    }

    public static final ResultTypes<ResultOne, Long, ResultOneBO> RESULT_ONE = new ResultTypes<ResultOne, Long, ResultOneBO>("Result One") {
        @Override
        public ResultOneService getService() {
            return serviceInitializer.resultOneService;
        }
    };
    public static final ResultTypes<ResultTwo, Long, ResultTwoBO> RESULT_TWO = new ResultTypes<ResultTwo, Long, ResultTwoBO>("Result Two") {
        @Override
        public ResultTwoService getService() {
            return serviceInitializer.resultTwoService;
        }
    };
    public static final ResultTypes<ResultThree, Long, ResultThreeBO> RESULT_THREE = new ResultTypes<ResultThree, Long, ResultThreeBO>("Result Three") {
        @Override
        public ResultThreeService getService() {
            return serviceInitializer.resultThreeService;
        }
    };

    protected String name;

    protected ServiceInitializer serviceInitializer;

    private ResultTypes(String name) {
        this.name = name;
    }

    protected void setServiceInitializer(ServiceInitializer serviceInitializer) {
        this.serviceInitializer = serviceInitializer;
    }

    @Component
    static class ServiceInitializer {
        @Autowired
        private ResultOneService resultOneService;

        @Autowired
        private ResultTwoService resultTwoService;

        @Autowired
        private ResultThreeService resultThreeService;

        @PostConstruct
        public void init() {
            for (ResultTypes resultType : ResultTypes.values()) {
                resultType.setServiceInitializer(this);
            }
        }
    }
}

我认为由于解决方案变得冗长,我将坚持使用 enum 方法,并接受这种边界损失.我必须添加自己的 values() 实现而失去的比我从强制执行这些界限中获得的要多.不过,这是一个有趣的理论练习,再次感谢您的帮助.

I think because of how lengthy the solution becomes, I'll stick with the enum approach, and just accept this loss of bounds. I lose more by having to add my own values() implementation than I gain from enforcing these bounds. However, this is an interesting theoretical exercise, and thank you again for your help.

推荐答案

好的,首先你需要明白为什么你正在做的事情可能不是你认为的那样.让我们看一个更简单的例子.

Okay, first you need to understand why what you're doing is probably not what you think it's doing. Let's look at a simpler example.

interface Face {
    <T> List<T> get();
}

你有一个通用方法,get.泛型方法的类型参数取决于调用站点提供的内容.例如像这样:

What you have there is a generic method, get. A generic method's type parameter depends on what is supplied by the call site. So for example like this:

Face f = ...;
// this call site dictates T to be Number
List<Number> l = f.<Number>get();

当你像覆盖它一样

class Impl implements Face {
    @Override
    public List<String> get() { return ...; }
}

这是您能够做到的事情(只是因为擦除),但您可能不应该.它只允许向后兼容非通用代码.你应该听警告而不是去做.这样做意味着例如我仍然可以过来并指示它返回其他东西:

This is something you are able to do (only because of erasure) but you probably shouldn't. It's only allowed for backwards compatibility to non-generic code. You should listen to the warning and not do it. Doing it means that for example I can still come along and dictate it to return something else:

Face f = new Impl();
// now I've caused heap pollution because you
// actually returned to me a List<String>
List<Number> l = f.<Number>get();

这就是未检查转换的原因.

This is why there is an unchecked conversion.

您可能的意思是使用通用接口声明:

What you probably meant is to use a generic interface declaration:

interface Face<T> {
    List<T> get();
}

现在 T 的参数取决于对象引用的类型.

Now the argument to T depends on the type of the object reference.

Face<Number> f = ...;
// get must return List<Number>
List<Number> l = f.get();

我们可以像这样实现它

class Impl implements Face<String> {
    @Override
    public List<String> get() { return ...; }
}

此外,您无法访问枚举上的协变返回类型.当您覆盖枚举常量的方法时,它的类是匿名的.匿名类没有名字,不能被引用.因此程序员无法知道它的协变返回类型来使用它.此外,枚举不能声明泛型类型参数.所以你想要做的事情对于 enum 来说是不可能的.

Additionally, you cannot access covariant return types on an enum. When you override methods on an enum constant, its class is anonymous. An anonymous class has no name and cannot be referred to. Therefore the programmer cannot know its covariant return type to use it. Furthermore, an enum cannot declare generic type parameters. So what you are wanting to do is simply impossible with enum.

您可以使用带有 public static final 实例的类来模拟通用枚举:

You can use a class with public static final instances to simulate a generic enum:

public abstract class SimEnum<T> implements Face<T> {
    public static final SimEnum<Number> A = new SimEnum<Number>() {
        @Override
        public List<Number> get() { return ...; }
    };
    public static final SimEnum<String> B = new SimEnum<String>() {
        @Override
        public List<String> get() { return ...; }
    };

    private SimEnum() {}

    public static SumEnum<?>[] values() {
        return new SimEnum<?>[] { A, B };
    }
}

否则你需要彻底改变你的想法.

Otherwise you need to drastically change your idea.

这篇关于Java泛型和枚举,模板参数丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆