当前位置: 首页 > 面试题库 >

Java泛型和枚举,模板参数丢失

郎伟兆
2023-03-14
问题内容

我的结构相当复杂,无法正常工作。这是我所做的:

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
    }
}

目的是根据枚举来概括调用,而只是能够迭代枚举数组

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

这工作正常且花花公子。但是,如果我说

ResultTypes.RESULT_TYPE_ONE.getService().getRepository()

那么它是一个BaseRepository<Object, Serializable>而不是一个BaseRepository<ResultTypeOne, Long>。该方法resultTypeHolder.getService()返回ResultService<M, ID, BO>,但最终它变为ObjectSerializable

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

我想补充一点,我确实意识到问题出在未经检查的转换中。但是服务定义为

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

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

编辑 :从技术上讲,如果我明确推断出它们,则可以使用:

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

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

EDIT2 :的超类ResultTypeOne

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

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

EDIT3 :非常感谢@Radiodef!理论解最终如下,并且可以很好地工作:

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()实现而遭受的损失比执行这些限制要多。但是,这是一个有趣的理论练习,再次感谢您的帮助。


问题答案:

好的,首先,您需要了解为什么您正在做的事情可能不是您认为的正在做的事情。让我们来看一个简单的例子。

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

您所拥有的是通用方法get。通用方法的类型参数取决于调用站点提供的内容。因此,例如:

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 ...; }
}

可以执行操作 (仅由于擦除操作),但 您可能不应该这样做
。仅允许向后兼容非通用代码。您应该听警告并且不要这样做。这样做意味着,例如,我仍然可以指示它返回其他内容:

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();

这就是为什么存在未经检查的转换的原因。

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

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

现在,to的参数T取决于对象引用的类型。

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

我们可以像

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

此外,您无法访问枚举上的协变返回类型。当您覆盖枚举常量上的方法时,其类为匿名。匿名类没有名称,因此无法引用。因此,程序员无法知道其协变返回类型来使用它。此外,枚举不能声明泛型类型参数。因此,用枚举根本无法实现您想做的事情。

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

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 };
    }
}

否则,您需要彻底改变自己的想法。



 类似资料:
  • 问题内容: 这件事让我困扰了一阵子。我之前曾问过一些问题,但措辞可能很拙劣,而且例子太抽象了。所以目前尚不清楚我实际上在问什么。我会再尝试。并且请不要下结论。我希望这个问题根本不容易回答! 为什么我不能在Java中使用带有泛型类型参数的枚举? 问题不在于语法上为什么不可能做到这一点。我知道这只是不受支持。问题是:为什么JSR人员会“忘记”或“忽略”这个非常有用的功能?我无法想象与编译器相关的原因,

  • 我写了以下方法 我如何将不同的枚举类型传递给第二个参数?我知道我不能创建枚举的实例,但初始化枚举意味着我将传递一个值,而不是整个初始化的枚举,如下所示...其他枚举也将传递给相同的方法以实现组合细节

  • 问题内容: 我有一个像这样的通用接口: 此接口具有有限的实例,因此最好将它们实现为枚举值。问题是那些实例具有不同类型的值,因此我尝试了以下方法,但无法编译: 有什么想法吗? 问题答案: 你不能 Java不允许在枚举常量上使用泛型类型。但是,它们允许用于枚举类型: 在这种情况下,你可以做的是为每个泛型类型都拥有一个枚举类型,或者通过将其设为一个类来“伪造”一个枚举: 不幸的是,它们都有缺点。

  • 本文向大家介绍详解Java中的 枚举与泛型,包括了详解Java中的 枚举与泛型的使用技巧和注意事项,需要的朋友参考一下 详解Java中的 枚举与泛型 一:首先从枚举开始说起 枚举类型是JDK5.0的新特征。Sun引进了一个全新的关键字enum来定义一个枚举类。下面就是一个典型枚举类型的定义:    显然,enum很像特殊的class,实际上enum声明定义的类型就是一个类。 而这些类都是类库中En

  • 问题内容: 更新: 感谢所有提供帮助的人-这个答案的答案在于我在更复杂的代码中没有注意到的内容以及对Java5协变量返回类型不了解的内容。 原始帖子: 今天早上我一直在玩一些东西。虽然我知道我 可以用 不同的方式解决整个问题,但我发现自己一直迷恋于弄清楚为什么它没有按我预期的那样工作。在花了一些时间阅读这些内容之后,我发现我离理解还很近,因此我将其作为一个问题来看看我是否只是愚蠢,或者是否真的有我