类型参数类与通用 Java

huangapple go评论77阅读模式
英文:

Type Parameter Class with generic java

问题

以下是翻译好的内容:

我尝试在Java 15中使用以下代码中的通用类:

@Singleton
public class GenericRepository<T> implements IGenericRepository<T>{
    private final MongoClient mongoClient;
    
    public GenericRepository(MongoClient mongoClient) {
        this.mongoClient = mongoClient;
    }

    public MongoCollection<T> getCollection(String collectionName) {
        return mongoClient
                .getDatabase("main")
                .getCollection(collectionName, T.class);
    }
}

我无法使用 T.class,我该如何解决这个问题?

我找到的解决方案是:

@Singleton
public class GenericRepository<T> implements IGenericRepository<T>{
    private final MongoClient mongoClient;
    private final Class<T> typeParameterClass;

    public GenericRepository(MongoClient mongoClient, Class<T> typeParameterClass) {
        this.mongoClient = mongoClient;
        this.typeParameterClass = typeParameterClass;
    }

    public MongoCollection<T> getCollection(String collectionName) {
        return mongoClient
                .getDatabase("main")
                .getCollection(collectionName, this.typeParameterClass);
    }
}

由于使用这个解决方案需要额外的代码,是否有更好的方法来解决这个问题?

英文:

I am trying to use the generic class in java 15 with the below code

@Singleton
public class GenericRepository&lt;T&gt; implements IGenericRepository&lt;T&gt;{
    private final MongoClient mongoClient;
    
    public GenericRepository(MongoClient mongoClient) {
        this.mongoClient = mongoClient;
    }

    public MongoCollection&lt;T&gt; getCollection(String collectionName) {
        return mongoClient
                .getDatabase(&quot;main&quot;)
                .getCollection(collectionName, T.class);
    }
}

I can't use T.class, how can I solve this

Solution I found

@Singleton
public class GenericRepository&lt;T&gt; implements IGenericRepository&lt;T&gt;{
    private final MongoClient mongoClient;
    private final Class&lt;T&gt; typeParameterClass;

    public GenericRepository(MongoClient mongoClient, Class&lt;T&gt; typeParameterClass) {
        this.mongoClient = mongoClient;
        this.typeParameterClass = typeParameterClass;
    }

    public MongoCollection&lt;T&gt; getCollection(String collectionName) {
        return mongoClient
                .getDatabase(&quot;main&quot;)
                .getCollection(collectionName, this.typeParameterClass);
    }
}

Since using this solution is quite extra code, is there any better way to do this ?

答案1

得分: 2

一个只有构造函数带有参数,并且是参数化的单例类?

你的代码毫无意义。你遇到的问题在其他情况下很重要,但在这种情况下却不重要。对于这个问题(问题是:泛型被擦除),没有直接的解决方案,但有不同的代码风格可以避免这个问题。

问题是,因为这个问题甚至不适用于这种情况,很难解释如何重写这段代码,使问题作为一般原则消失。

在这种情况下,你只需要... 移除类型参数,摆脱那个接口(通常如果你有一个名为IFoo的接口,有些地方不对劲),就是这么简单。

如果你想要概括这个概念,使你可以创建很多这样的类(比如说你有一个用来检索Foo的类,另一个用来检索Bar的类),你可以在public class FooFetcher implements GenericFetcher<X>中获取<X>,虽然有点棘手(你会从你自己的java.lang.Class中使用getGenericSuper,然后从那里开始处理。在这种情况下有很多注意事项,所以我不会在这方面做更多展开,只需知道这样做是如何的。

如果在另一种情况下,你确实需要以可在运行时查询的方式传达泛型,你的风格问题在于“类对象”和“泛型参数”重叠但并不相同。int有一个类对象(int.class),但List<int>不是有效的Java代码。List<String>是有效的泛型(List<List<String>> x;是有效的Java代码),但List<String>.class不是一个存在的东西,而且永远也不会是,只有List.class可以。对于?? extends Map<?, List<? extends Number>> & Serializable,这是有效的泛型,但很明显无法用java.lang.Class类型表示。因此,如果你确实希望泛型作为一种可在运行时查询的概念,可以在网络上搜索“超类型标记”(super type tokens) - 但请注意,它们需要比你这里的代码更多的代码,而不是更少。它们实际上能够精确地表示泛型所能表示的内容。

作为一个一般的经验法则,如果你依赖于java.lang.Class的泛型来将你的代码粘合在一起,你肯定做错了什么,你可能需要使用工厂模式。

英文:

A singleton class, whose only constructor takes parameters, and is parameterized?

Your code makes no sense. The problem you're running into is significant in other contexts, but not this one. There is no direct solution to this problem (the problem being: Generics are erased), but there are different code styles that avoid it.

The problem is, because the problem doesn't even apply to this situation, it's hard to explain how one would rewrite this code so the problem goes away as a general principle.

Here, you'd just... remove the type param, get rid of that interface (in general if you have IFoo hanging around, something's not right), simple as that.

If you want to generalize this concept, so that you can make a lot of these (Let's say you have one of these classes to retrieve the Foos, and another to retrieve the Bars), you CAN fetch the &lt;X&gt; in specifically public class FooFetcher implements GenericFetcher&lt;X&gt;, though it's a bit tricky (you'd use getGenericSuper from your own java.lang.Class and take it from there. There's a ton of caveats in this situation, so I won't expand any further on this, just know that's how you could do it.

If, in a different situation, you do need to convey generics in a runtime-queryable way, the problem with your style is that a class object and a generics param overlap but aren't the same. int has a class object (int.class), but List&lt;int&gt; is not valid java code. List&lt;String&gt; is valid generics (List&lt;List&lt;String&gt;&gt; x; is valid java), but List&lt;String&gt;.class isn't a thing and never will be, only List.class can be. Same goes for ?, ? extends Map&lt;?, List&lt;? extends Number&gt;&gt; &amp; Serializable, which is valid generics, but rather obviously not at all something you can represent with a java.lang.Class type. Therefore, if you do want generics as a runtime queryable concept, search the web for 'super type tokens' - but note that they require MORE code than what you have here, not less. They're just actually capable of representing exactly what generics can represent.

As a general rule of thumb if you're relying on the generics of java.lang.Class to glue your code together you've done something wrong, and you probably want factories.

huangapple
  • 本文由 发表于 2020年10月23日 16:37:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/64496689.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定