如何动态转换/确保具有不同泛型的集合的类型安全

发布于 2024-12-07 00:35:13 字数 1123 浏览 0 评论 0 原文

我决定在我的设计中使用泛型,效果很好。只有当我达到抽象时,我才陷入困境。我正在收集使用不同泛型实现相同接口的不同实例。据我所知,为了将它们保存在列表中,我需要使用通配符。所以我当前的代码是这样的:

import java.util.List;
import java.util.ArrayList;


public class Main {
    private List<IView<?>> views = new ArrayList<IView<?>>();


    public void addView(IView<?> view)
    {
        this.views.add(view);
    }

    public void handle()
    {
        for (IView<?> view : this.views)
        {       
            // ## ERROR ##
            view.render(
                    view.cast(new Object())
                );
        }
    }
}

public interface IView<T> { 
    public T cast(Object entity);
    public void render(T entity);
}

public class FirstView implements IView<One> {  
    // .. Added to Main-collection views
}

public class SecondView implements IView<Two> { 
    // .. Added to Main-collection views
}

我还尝试了另一种方法,从 IView 返回一个类,然后用它来调用 class.cast。同样的问题。

编译器不接受这一点 - IView(Capture#2-of ?) 类型中的 Render(Capture#2-of ?) 方法不适用于参数 (Capture#3-of ?)。

我部分地想理解这个问题,但我看不出有什么办法解决它。如果有人能帮助我重新开始,我将非常感激。

I've decided to use generics in my design, which works fine. Only when I get to abstraction, I got stuck. I'm collecting different instances implementing the same interface with a different generics. To save those in a list, I need to use wildcards, as far as I know. So my current code is like this:

import java.util.List;
import java.util.ArrayList;


public class Main {
    private List<IView<?>> views = new ArrayList<IView<?>>();


    public void addView(IView<?> view)
    {
        this.views.add(view);
    }

    public void handle()
    {
        for (IView<?> view : this.views)
        {       
            // ## ERROR ##
            view.render(
                    view.cast(new Object())
                );
        }
    }
}

public interface IView<T> { 
    public T cast(Object entity);
    public void render(T entity);
}

public class FirstView implements IView<One> {  
    // .. Added to Main-collection views
}

public class SecondView implements IView<Two> { 
    // .. Added to Main-collection views
}

I also tried an alternative approach returning a Class from the IView which I then use to call the class.cast. Same problem.

This is NOT accepted by the compiler
- The method Render(Capture#2-of ?) in the type IView(Capture#2-of ?) is not applicable for the arguments (Capture#3-of ?).

I partially think to understand the problem, but I see no way of solving it. It would be very much appriciated if someone could help me getting going again.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦太阳 2024-12-14 00:35:13

我不相信您可以为 ? “类型化”变量赋值。一个无限的“?”意味着“变量的原始类型未知”,这意味着没有已知类型可以安全地分配给它。这并不意味着在运行时检索类型参数的实际值。

使用泛型方法进行强制转换的整个想法在 Java 泛型中没有意义。以类型安全的方式对未知类型对象进行强制转换的唯一方法是使用实​​际强制转换。

唯一的解决方案是摆脱cast()方法,并使用原始类型:

public void handle()
{
    for (IView view : this.views)
    {
        view.render(new Object());
    }
}

假设cast()方法只进行强制转换,而不进行类型转换。如果最终调用的实现方法是 FirstView.render(One object),我相信 Java 实际上会将参数强制转换为 One。 (这也可能导致有趣的错误,即在没有强制转换的代码行上抛出 ClassCastException。)

如果 cast() 可以进行类型转换(在这种情况下,“cast”是一个坏名字对于操作),这意味着每个 IView必须能够接受对象,在这种情况下,将 render() 的参数设置为没有多大意义首先是泛型类型。我将使用的设计如下:

interface IView<T> {
    void renderObject(Object o);
    void render(T);
    T coerce(Object o);
}

abstract class ViewBase<T> implements IView<T> {
    void renderObject(Object o) {
        render(coerce(o));
    }
}

class FirstView extends ViewBase<One> {
    // …
}

封装“cast”操作。

没有静态类型安全的方法来处理不同类型的对象集合 - 在 Java 中,IViewIView 的类型与 一样不同>StringInteger 并且两者之间不存在安全转换。

I don't believe you can assign a value to an ? "typed" variable. An unbounded "?" means "the original type of the variable is unknown", which means there is no known type it's safe to assign to it. It doesn't mean that the actual value of the type parameter is retrieved at runtime.

The entire idea of using a generic method to do a cast doesn't make sense in Java generics. The only way to do a cast of an unknown type object in a type-safe way is with an actual cast.

The only solution is to get rid of the cast() method, and use raw types:

public void handle()
{
    for (IView view : this.views)
    {
        view.render(new Object());
    }
}

Assuming the cast() method is only doing casting and not type conversion. If the implementing method that ends up being called is FirstView.render(One object), I believe Java will actually cast the parameter to One. (This can also cause amusing bugs where a ClassCastException is thrown on a line of code with no casts.)

If cast() can do a type conversion (in which case, "cast" is a bad name for the operation), that means that every IView must be able to accept Object, in which case it doesn't make much sense have the parameter of render() be the generic type to begin with. The design I would use is something like the following:

interface IView<T> {
    void renderObject(Object o);
    void render(T);
    T coerce(Object o);
}

abstract class ViewBase<T> implements IView<T> {
    void renderObject(Object o) {
        render(coerce(o));
    }
}

class FirstView extends ViewBase<One> {
    // …
}

encapsulating the "cast" operation.

There is no statically typesafe way to work with a collection of objects of different types - in Java, IView<One> and IView<Two> are types as different as String and Integer and there are no safe conversions between the two.

妄想挽回 2024-12-14 00:35:13

由于我想知道代码 view.render(view.cast(new Object()); 中的两个捕获,
我阅读了Angelika Langer 的泛型常见问题解答中的相应部分。它指出:

我们不能通过无界通配符参数化来调用方法
接受“未知”类型参数的类型。

时期。

因此,使用通配符声明可以防止使用参数化参数,即使类型推断很简单,就像您的情况一样:( 因此 view.cast(new Object(); 是允许的,但 view .render(view.cast(new Object()); 不是。


为了通过示例进行说明,Angelika 给出了以下内容:

Box<?> box = new Box<String>("abc");

box.contains("abc");    // error 

其中 contains 将 Box 的参数化类型作为参数她说:

如果通过引用变量执行该调用是非法的
输入 Box 。 [将 contains 方法定义为采用以下参数
输入 Object 而不是 T 可以避免这种效果。在这种情况下
contains 方法不会采用“未知”的对象,而是采用
“任何”类型的对象,并且允许通过
Box 类型的引用变量 .]

因此,她建议返回到 Object (类似于我其他答案中的建议)。顺便说一句,她不鼓励使用原始类型,正如 Josh Bloch 在 Effective Java's 项目中所做的那样23、“不要在新代码中使用原始类型”。

Since I was wondering about the two captures in code view.render(view.cast(new Object());,
I read the appropriate section in Angelika Langer's Generics FAQ. It states that:

We cannot call methods through an unbounded wildcard parameterized
type that take arguments of the "unknown" type.

PERIOD.

So using a wildcard declaration prevents the use of parameterized arguments, even if type inference were simple, as in your case :( Hence view.cast(new Object(); is allowed, but view.render(view.cast(new Object()); isn't.


Just for clarification via an example, Angelika gives the following:

Box<?> box = new Box<String>("abc");

box.contains("abc");    // error 

where contains takes as argument the parameterized type of Box. She states:

The invocation is illegal if performed through a reference variable of
type Box<?> . [Defining the contains method as taking an argument of
type Object , instead of T , would avoid this effect. In this case
the contains method would not take an object of "unknown", but an
object of "any" type, and it would be permitted to invoke it through a
reference variable of type Box<?> .]

So she suggests retrieting back to Object (similarly to the suggestion in my other answer). Btw, she discourages using raw types, as does Josh Bloch in Effective Java's item 23, "Don’t use raw types in new code".

莫相离 2024-12-14 00:35:13

我不明白编译器错误,即为什么 view.render(view.cast(new Object()); 生成两个捕获。(我已经用 view 是最终的,并且显示相同的错误消息)。

但是由于 IViewIView 仅将 Object 作为通用类型。 ,解决的方法是使用列表>

I do not understand the compiler error, i.e. why view.render(view.cast(new Object()); produces two captures. (I've tried this code with view being final, and the same error message shows).

But since IView<One> and IView<Two> only have Object as common type, the way of solving it is using List<IView<Object>>.

相思故 2024-12-14 00:35:13

我通过返回一个类型化的对象来替换了转换(并且我通过用 IEntity 替换新的 Object 稍微改变了示例);

public class Main {
    public static List<IView<? extends IEntity<?>>> views = new ArrayList<IView<? extends IEntity<?>>>();

    public static <T extends IEntity<T>> void register(IView<T> view)
    {
        Main.views.add(view);
    }


    public static void handle()
    {
        for (IView<? extends IEntity<?>> view : Main.views)
        {
        // ## Error ##
        /* Bound mismatch: The generic method view(IView<T>) of the type Main 
         * is not applicable for the arguments (IView<capture#1-of ? extends IEntity<?>>). 
         * The inferred type capture#1-of ? extends IEntity<?> is not a valid 
         * substitute for the bounded parameter <T extends IEntity<T>> 
         */             
            Main.view(view);
        }
    }

    public static <T extends IEntity<T>> void view(IView<T> view)
    {
        IKey<T> key = view.getKey(/* Some arg */);
        T entity = key.getEntity();

        view.render(entity);
    }

}

public interface IView<T> {
    public IKey<T> getKey(/* Some args */);
    public void render(T entity);
}

public interface IKey<T> {
    public T getEntity();
}

通配符在某种程度上是有界的,但它会产生另一个问题(实际上可能是相同的):有没有办法检查两个通配符是否匹配,即:

for (IView<? extends IEntity<?>> view : Main.views)

Where?只能是一种(=相同)(通用)类型?

I sort of replaced the casting by returning a typed object (and I slightly changed the example by replacing the new Object with IEntity);

public class Main {
    public static List<IView<? extends IEntity<?>>> views = new ArrayList<IView<? extends IEntity<?>>>();

    public static <T extends IEntity<T>> void register(IView<T> view)
    {
        Main.views.add(view);
    }


    public static void handle()
    {
        for (IView<? extends IEntity<?>> view : Main.views)
        {
        // ## Error ##
        /* Bound mismatch: The generic method view(IView<T>) of the type Main 
         * is not applicable for the arguments (IView<capture#1-of ? extends IEntity<?>>). 
         * The inferred type capture#1-of ? extends IEntity<?> is not a valid 
         * substitute for the bounded parameter <T extends IEntity<T>> 
         */             
            Main.view(view);
        }
    }

    public static <T extends IEntity<T>> void view(IView<T> view)
    {
        IKey<T> key = view.getKey(/* Some arg */);
        T entity = key.getEntity();

        view.render(entity);
    }

}

public interface IView<T> {
    public IKey<T> getKey(/* Some args */);
    public void render(T entity);
}

public interface IKey<T> {
    public T getEntity();
}

The wildcard is someway bounded, but then it creates another problem (which may actually be the same): Is there a way to check that two wildcards match, i.e.:

for (IView<? extends IEntity<?>> view : Main.views)

Where ? can only be one (=the same) (generic)type?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文