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

.NET是否等效于Java有界通配符(IInterf <?>)?

满俊楠
2023-03-14
问题内容

我一直试图将一些使用(有界)通配符泛型的Java代码转换为C#。我的问题是,Java与通配符一起使用时似乎允许泛型既协变又协变。

[这是从先前的问题中衍生出来的,该问题处理的是更简单的有界通配符案例]

Java-作品:

class Impl { }

interface IGeneric1<T extends Impl> {
    void method1(IGeneric2<?> val);
    T method1WithParam(T val);
}

interface IGeneric2<T extends Impl> {
    void method2(IGeneric1<?> val);
}

abstract class Generic2<T extends Impl> implements IGeneric2<T> {

    // !! field using wildcard 
    protected IGeneric1<?> elem;

    public void method2(IGeneric1<?> val1) {
        val1.method1(this);

        //assignment from wildcard to wildcard
        elem = val1;
    }
}

abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> {

    public void method1(IGeneric2<?> val2) {
        val2.method2(this);
    }
}

C# -无法编译…

class Impl { }

interface IGeneric1<T> where T:Impl {
  //in Java:
  //void method1(IGeneric2<?> val);
    void method1<U>(IGeneric2<U> val) where U : Impl; //see this Q for 'why'
                                 // https://stackoverflow.com/a/14277742/11545

    T method1WithParam(T to);
}

interface IGeneric2<T>where T:Impl {
    void method2<U>(IGeneric1<U> val) where U : Impl;
}

abstract class Generic2<T, TU>: IGeneric2<T> //added new type TU
    where T : Impl
    where TU : Impl
{
  //in Java:
  //protected IGeneric1<?> elem;
    protected IGeneric1<TU> elem;

  //in Java:
  //public void method2(IGeneric1<?> val1) 
    public void method2<U>(IGeneric1<U> val) 
        where U : TU //using TU as constraint
    {
        elem = val;  //Cannot convert source type 'IGeneric1<U>' 
                     //to target type 'IGeneric1<TU>'
    }
    public abstract void method1WithParam(T to);
}

abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl
{
  //in Java:
  //public void method1(IGeneric2<?> val2) 
    public void method1<U>(IGeneric2<U> val2) where U : Impl
    {
         val2.method2(this);
    }

    public abstract T method1WithParam(T to);
    public abstract void method2<U>(IGeneric1<U> val) where U : Impl;
    public abstract void nonGenericMethod();
}

如果我更改interface IGeneric1 为interface IGeneric1 上述错误,错误会消失,但会method1WithParam(T)抱怨差异:

Parameter must be input-safe. Invalid variance: The type parameter 'T' must be
contravariantly valid on 'IGeneric1<out T>'.

问题答案:

首先,我要说的是,设计审查肯定已经开始。原始的Java类聚集一个IGeneric1<?>成员,但是在不知道其类型参数的情况下,不可能以method1WithParam类型安全的方式对其进行调用。

这意味着elem只能用于调用其method1成员,该成员的签名不依赖于的类型参数IGeneric1。因此,method1可以将其分解为一个非通用接口:

// C# code:
interface INotGeneric1 {
    void method1<T>(IGeneric2<T> val) where T : Impl;
}

interface IGeneric1<T> : INotGeneric1 where T : Impl {
    T method1WithParam(T to);
}

之后,class Generic2可以聚合INotGeneric1成员:

abstract class Generic2<T>: IGeneric2<T> where T : Impl
{
    protected INotGeneric1 elem;

    // It's highly likely that you would want to change the type of val
    // to INotGeneric1 as well, there's no obvious reason to require an
    // IGeneric1<U>
    public void method2<U>(IGeneric1<U> val) where U : Impl
    {
        elem = val; // this is now OK
    }
}

当然,现在,elem.method1WithParam除非您诉诸强制转换或反射,否则就无法调用,即使已知存在这样的方法并且该方法是通用的,并且带有一些未知类型X作为类型实参。但是,这与Java代码的限制相同。只是C#编译器不会接受此代码,而Java仅在您尝试调用时才会抱怨method1WithParam1。



 类似资料:
  • 问题内容: 我一直试图将一些使用(有界)通配符泛型的Java代码转换为C#。我的问题是,Java与通配符一起使用时似乎允许泛型既协变又协变。例如: Java: …作品。 等价于C#(?) …无法编译-请查看注释中的错误。这是可以预料的,因为IGeneric的泛型参数未标记为“ out”用于协方差。 如果我更改此: 对此 错误消失了,但出现了另一个错误,因为该方法的声明在同一接口内采用了通用参数:

  • 问题内容: 我发现.Net FileSystemWatcher类非常适合编写实用程序,当文件显示在其监视的文件夹中时,这些实用程序会自动启用。* nix世界中是否有与此功能等效的功能,可以让我监视文件夹(可能还有其所有子目录)? 编辑: 最好是不需要内核补丁的东西。 问题答案: 那就是Gamin文件变更监视器或Inotify。 编辑:Mono确实具有Gamin绑定-实际上,其FileSystemW

  • 问题内容: 我对Java的异常包不如.NET那样熟悉。我处于一种情况,如果使用C#编程,我会抛出。 在创建自己的子类之前,我需要知道是否应该在Java中抛出类似的异常类型。 确切的情况是: 我的类是一个提供方法的值对象,该方法返回一个int值。但是,在某些情况下,当前值不能作为int提供,因此此类还提供了一个允许API用户知道何时可以安全地调用它们的方法。 如果主叫用户呼叫时是一个异常应该被抛出。

  • 问题内容: 我是一名.Net开发人员,开始为Android开发Java,并想知道考虑诸如.Net程序集之类的Java软件包是否正确。 问题答案: 没有。 最好的比较是与Java ARchive(Jar)文件。Java使用包来控制名称空间,并且与C#的名称空间非常相似。 这是我比较环境的方式

  • 根据Joshua Bloch的“有效Java”一书,关于如何/何时在泛型中使用有界通配符有一个规则。这个规则就是PECS(productor-extends,Comsumer-Super)。当我研究以下示例时: 根据PECS规则,上述声明是错误的。但是我希望有一个的,并向这个传递一个。为什么不做呢? 为什么要始终使用关键字?为什么使用是错误的? 当然,这也代表了Comsumer的观点。为什么消费者

  • 我正在与Java8通配符泛型作斗争。 假设一个名为的泛型类(来自Core Java book) 是因为Java8编译器转换吗?超级经理反对,因此任何事情都是允许的?