我有以下界面:

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是否应该表现出相同的行为?看起来似乎并不一致。

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

从接口(interface)中删除<T extends User>并将其应用于这样的方法:
<U extends User> List<U> findAll(UserCriteria userCriteria, PageDetails pageDetails);

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

我还尝试过从接口(interface)和方法中删除泛型,同时保持返回列表的泛型:
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找不到以JpaUserRepository为UserRepository<JpaUser>进行 Autowiring 的bean。服务层不知道存储库的实现。

最佳答案

如果您演示了如何声明userRepository,这将有所帮助,因为缺少该声明。但是问题在于,存储库具有通用类型<T extends User>,这与<User>不同,这就是为什么您的代码发出警告的原因。

问题不仅仅在于警告,还在于泛型类型的正确用法。我猜你的userRepository声明如下:

@Autowired
private JpaUserRepository userRepository;

但这意味着名称不正确。毕竟,它不是User对象的存储库,而是JpaUser对象的存储库。

现在,您可能会说JpaUser是从User派生的。但这不是Java泛型的工作方式。那里有一个很好的资源Java Generics FAQ。确实值得一读。

现在,我将在某种程度上进行推测,因为我认为您是在向用户公开存储库,但您不想公开JpaUser类。但这没有任何意义,因为存储库是一个非常基本的接口(interface),您不应该在库中公开它。

因此,在没有所有信息的情况下,通过进行有根据的猜测,我可以看到两种情况:
  • 您不会向用户公开存储库,在这种情况下,您只需要处理JpaUser对象即可。
  • 您确实希望用户使用User对象,在这种情况下,您应该构建一个外观以隐藏存储库。

  • 编辑:我已经快速制作了一个立面类,您可能希望将其用作起点。
    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());
    }
    

    它在编译时不会发出任何警告,因为编译器会推断出正确的类型。我承认我发现它缺乏一定的优雅,但确实有效。

    10-08 00:37