代码之家  ›  专栏  ›  技术社区  ›  Sikor

如何在不取消选中赋值的情况下从通用接口检索对象列表?

  •  8
  • Sikor  · 技术社区  · 6 年前

    我有以下界面:

    public interface UserRepository<T extends User> {
        List<T> findAll(UserCriteria userCriteria, PageDetails pageDetails);
        T findByEmail(String email);
    }
    

    及其实施:

    @Repository
    public class JpaUserRepository implements UserRepository<JpaUser> {
        public List<JpaUser> findAll(UserCriteria userCriteria, PageDetails pageDetails) {
           //implementation
        }
    
        public JpaUser findByEmail(String email) {
           //implementation
        }
    }
    

    现在,当我打电话时:

    User user = userRepository.findByEmail(email);
    

    在我的服务课上,一切都很好。

    但当我打电话时:

    List<User> users = userRepository.findAll(userCriteria, pageDetails);
    

    我收到未检查的分配警告,原因是userRepository具有原始类型,所以findAll的结果被擦除。如果真是这样的话 findByEmail 表现得一样吗?只是看起来不太一致。

    在这种情况下,如何消除原始类型?我尝试了几件事:

    正在删除 <T extends User> 并将其应用于如下方法:

    <U extends User> List<U> findAll(UserCriteria userCriteria, PageDetails pageDetails);
    

    这对服务来说很好,但存储库实现现在会发出关于未经检查的覆盖的警告(返回类型需要未经检查的转换)。

    我还尝试从接口和方法中删除泛型,同时保持返回列表为泛型:

    List<? extends User> findAll(UserCriteria userCriteria, PageDetails pageDetails);
    

    这解决了问题,没有警告,但要求我这样编写服务:

    List<? extends User> users = userRepository.findAll(userCriteria, pageDetails);
    

    而且它感觉有点笨重(也许只是我,所以请让我知道,从“良好编程”的角度来看,这是否可以接受)。

    不管怎样,有没有可能 List<User> 没有原始类型警告,同时保持存储库不变?

    非常感谢您抽出时间。

    编辑: 我不是在寻找一种方法来发布这个列表。

    编辑2: 存储库声明:

    private final UserRepository userRepository;
    

    你们中的一些人建议将该声明更改为 UserRepository<User> userRepository; 这成功地删除了警告,但Spring无法找到以这种方式自动连接的bean,因为JpaUserRepository是这样的 UserRepository<JpaUser> 。服务层不了解存储库实现。

    2 回复  |  直到 6 年前
        1
  •  4
  •   SeverityOne    6 年前

    如果你能展示一下 userRepository 已声明,因为缺少。但问题是存储库具有泛型类型 <T extends User> ,这是 与…一样 <User> ,这就是代码发出警告的原因。

    问题不在于警告,而在于泛型类型的正确使用。我猜你的 用户存储库 声明如下:

    @Autowired
    private JpaUserRepository userRepository;
    

    但这意味着名称是错误的。毕竟,它不是 User 对象,但属于 JpaUser 对象。

    现在,你可以说 JpaUser公司 派生自 使用者 .但Java泛型不是这样工作的。有一个很好的资源 Java Generics FAQ .真的很值得一读。

    现在我想推测一下,在某种意义上,我认为您是在向用户公开存储库,但您不想公开 JpaUser公司 班但这没有意义,因为存储库是一个非常基本的接口,不应该在库中公开。

    因此,在没有所有信息的情况下,通过有根据的猜测,我可以看到两种情况:

    1. 您不向用户公开存储库,在这种情况下,您只需处理 JpaUser公司 对象。
    2. 您确实希望用户使用 使用者 对象,在这种情况下,您应该构建一个隐藏存储库的立面。

    编辑:我很快创建了一个fa§ade类,您可能希望将其用作起点。

    public class RepositoryFacade {
    
        private final UserRepository<? extends User> repository;
    
        public RepositoryFacade(UserRepository<? extends User> repository) {
            this.repository = repository;
        }
    
        public List<User> findAll(final UserCriteria userCriteria, final PageDetails pageDetails) {
            return repository.findAll(userCriteria, pageDetails)
                    .stream()
                    .collect(Collectors.toList());
        }
    
        public User findByEmail(final String email) {
            return repository.findByEmail(email);
        }
    }
    
    public RepositoryFacade getJpaUserFacade() {
        return new RepositoryFacade(new JpaUserRepository());
    }
    

    它编译时没有任何警告,因为编译器推断出正确的类型。我承认我觉得它缺乏一定的优雅,但它很管用。

        2
  •  0
  •   Jeffrey Phillips Freeman    6 年前

    以下各项应无例外地正常工作:

    UserRepository<user> userRepository = ...
    List<User> users = userRepository.findAll(userCriteria, pageDetails);
    

    作为补充说明,与您的问题无关,您可能需要考虑按以下方式定义您的类:

    @Repository
    public class JpaUserRepository implements UserRepository<? extends JpaUser> {
        public List<? extends JpaUser> findAll(UserCriteria userCriteria, PageDetails pageDetails) {
           //implementation
        }
    
        public JpaUser findByEmail(String email) {
           //implementation
        }
    }
    

    由于返回的列表应该是不可修改的,因此它在实现中为您提供了更大的灵活性。它允许子类返回包含JpaUser子类的列表

    所以,例如,如果有一个名为AwesomeJpaUser的子类,那么子类可以安全地返回List。对于您当前的实现,这是不允许的。