Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么可以';“我扩展了一个接口”;“通用方法”;并将其类型缩小为“我继承的接口”;“通用”类;?_Java_Generics_Inheritance_Polymorphism_Multiple Inheritance - Fatal编程技术网

Java 为什么可以';“我扩展了一个接口”;“通用方法”;并将其类型缩小为“我继承的接口”;“通用”类;?

Java 为什么可以';“我扩展了一个接口”;“通用方法”;并将其类型缩小为“我继承的接口”;“通用”类;?,java,generics,inheritance,polymorphism,multiple-inheritance,Java,Generics,Inheritance,Polymorphism,Multiple Inheritance,我举了一个例子来说明我的意思,这个例子更简单。 想象通用类型C意味着颜色类型:对于 视觉简化假设C是C扩展颜色 interface Screen { <C> Background<C> render(Plane<C> plane); } interface MonochromeScreen<C> extends Screen{ @Override Background<C> render(Plane&

我举了一个例子来说明我的意思,这个例子更简单。 想象通用类型C意味着颜色类型:对于 视觉简化假设C是C扩展颜色

interface Screen {
   <C> Background<C> render(Plane<C> plane);
}

interface MonochromeScreen<C> extends Screen{
       @Override
       Background<C> render(Plane<C> plane);  
}
界面屏幕{
背景渲染(平面);
}
界面单色屏幕扩展屏幕{
@凌驾
背景渲染(平面);
}
这将引发名称冲突编译错误,解释两者具有相同的类型擦除,但不可重写

但我无法理解为什么我们不能简单地允许覆盖签名,只要它更具限制性。我的意思是,毕竟,唯一的区别是泛型类型的范围,在Screen中是方法范围,在单色Screen中是类范围

当其父方法在类级别强制一致性时,允许子方法作为方法作用域的泛型进行重写是没有意义的,但我认为情况并非如此:我的父接口可能有20个具有不相关泛型类型的方法,但我的子类会强制它们都与非不兼容的额外规范/契约相同(这是任何扩展接口所做的),毕竟,单色屏幕仍然是一个屏幕,因为它可以用任何颜色绘制,我只是强制使用该颜色,无论是哪种颜色,为了使它与子函数的其他函数保持一致,只需缩小类级别的可能性,而不是方法级别的可能性

考虑该特性是否存在根本错误的假设


编辑:我接受了Sotirios Delimanolis的回答,因为他非常聪明地发现了正确的问题,我不是在寻求解决方案,但对于那些想知道如何克服这种情况的人,我自己的回答中解释了一个技巧

我认为你的代码不是你想要的,这就是为什么会出现错误

interface Screen {
   <C> Background<C> render(Plane<C> plane);
}

interface MonochromeScreen<C> extends Screen{
       @Override
       Background<C> render(Plane<C> plane);  
}
我想这就是你想要的

public interface Screen<C> {
    Background<C> render(Plane<C> plane);
}
公共界面屏幕{
背景渲染(平面);
}

公共界面单色屏幕扩展屏幕{
背景渲染(平面);
}
您可能会错误地认为,因为
是两个接口,所以它是同一件事。事实并非如此

这个

公共界面单色屏幕扩展屏幕{
背景渲染(平面);
}
与上面的代码完全相同。C和HI只是通用占位符的名称。通过使用
extensdscreen
扩展
Screen
来扩展
Screen
,我们告诉java,C和HI是同一个占位符,因此它将发挥神奇的作用

在代码中

<C> Background<C> render(Plane<C> plane);
背景渲染(平面);
我们已经声明了一个全新的占位符,该占位符只包含该方法中的上下文。 所以我们可以写这个代码

MonochromeScreen<String> ms;
ms.render(new Plane<Banana>());

<C> Background<C> render(Plane<C> plane);
单色屏幕ms;
ms.render(新平面());
背景渲染(平面);
被重新定义为

Background<Banana> render(Plane<Banana> plane);
背景渲染(平面);
但是

背景渲染(平面);
被重新定义为

Background<String> render(Plane<String> plane);
背景渲染(平面);

哪一个冲突,java会给您一个错误。

这里是中断的地方:

MonochromeScreen<Red> redScreen = ...;
Screen redScreenJustAScreen = redScreen;
Plane<Blue> bluePlane = null;
redScreenJustAScreen.<Blue>render(bluePlane);
单色屏幕红屏=。。。; 屏幕RedScreenJustAsScreen=redScreen; 平面蓝平面=空; redScreenJustAScreen.render(蓝色平面); 如果您的建议在编译时起作用,那么上面的代码段可能会在运行时失败,出现
ClassCastException
,因为
redScreenJustAScreen
引用的对象需要一个
平面
,但收到一个
平面

如果使用得当,泛型应该可以防止上述情况发生。如果允许这样的覆盖规则,泛型将失败


我对您的用例了解不够,但似乎并不真正需要泛型。

不允许这样做的原因是它违反了规则

现在如果我们看一下单色屏幕:

interface MonochromeScreen<C> extends Screen{
   Background<C> render(Plane<C> plane);  
}
因此,
Screen s=新单色屏幕()不是有效的强制转换,
单色屏幕
不能是
屏幕
的子类


好吧,让我们扭转一下。让我们假设所有颜色都是单个
Color
类的实例,而不是单独的类。那么我们的代码会是什么样子呢

interface Plane {
    Color getColor();
}

interface Background {
    Color getColor();
}

interface Screen {
   Background render(Plane plane);
}
到目前为止,一切顺利。现在我们定义单色屏幕:

class MonochromeScreen implements Screen {
    private final Color color; // this is the only colour we have
    public Background render(Plane plane) {
        if (!plane.getColor().equals(color))
           throw new IllegalArgumentException( "I can't render this colour.");
        return new Background() {...}; 
    }
}
这将可以很好地编译,并且具有或多或少相同的语义

问题是:这是好代码吗?毕竟,您仍然可以这样做:

public void renderPrimaryPlanes(Screen s) { //this looks like a good method
    s.render(new Plane(Color.RED));
    s.render(new Plane(Color.GREEN));
    s.render(new Plane(Color.BLUE));
}

...
Screen s = new MonochromeScreen(Color.RED);
renderPrimaryPlanes(s); //this would throw an IAE
不,这绝对不是你从一个无辜的
renderPrimaryPlanes()
方法中所期望的。事情会以意想不到的方式破裂。为什么呢


这是因为尽管它在形式上有效且可编译,但这段代码也以与原始代码完全相同的方式破坏了LSP。问题不在于语言,而在于模型:您调用的
Screen
实体比名为
单色Screen
的实体可以做更多的事情,因此它不能是它的超类。

FYI:这是我找到的解决问题并通过重写方法的唯一方法(如果设计模式有一个名称,我很想知道ir!这最终是通过泛型方法扩展接口,使其成为类泛型。而且您仍然可以使用父类型(aka Color)键入它,将其用作原始的通用类型旧接口。):

Screen.java

public interface Screen {
    public interface Color {}
    public class Red  implements Color {}
    public class Blue implements Color {}

    static Screen getScreen(){
        return new Screen(){};
    }
    default <C extends Color> Background<C> render(Plane<C> plane){
        return new Background<C>(plane.getColor());
    }
}
interface MonochromeScreen<C extends Color> extends Screen{

    static <C extends Color> MonochromeScreen<C> getScreen(final Class<C> colorClass){
        return new MonochromeScreen<C>(){
            @Override public Class<C> getColor() { return colorClass; };
        };
    }

    public Class<C> getColor();

    @Override
    @SuppressWarnings("unchecked")
    default Background<C> render(@SuppressWarnings("rawtypes") Plane plane){
        try{
            C planeColor = (C) this.getColor().cast(plane.getColor());
            return new Background<C>(planeColor);
        } catch (ClassCastException e){
            throw new UnsupportedOperationException("Current screen implementation is based in mono color '" 
                                                   + this.getColor().getSimpleName() + "' but was asked to render a '"
                                                   + plane.getColor().getClass().getSimpleName() + "' colored plane" );
        }
    }
}
public class Plane<C extends Color> {   
    private final C color;
    public Plane(C color) {this.color = color;}
    public C getColor()   {return this.color;}
}
public class Background<C extends Color> {  
    private final C color;
    public Background(C color) {this.color = color;}
    public C getColor()        {return this.color;}
}
public class MainTest<C> {

    public static void main(String[] args) {

        Plane<Red> redPlane   = new Plane<>(new Red());
        Plane<Blue> bluePlane = new Plane<>(new Blue());

        Screen coloredScreen = Screen.getScreen();
        MonochromeScreen<Red> redMonoScreen = MonochromeScreen.getScreen(Red.class);
        MonochromeScreen<Color> xMonoScreen = MonochromeScreen.getScreen(Color.class);

        Screen redScreenAsScreen = (Screen) redMonoScreen;

        coloredScreen.render(redPlane);
        coloredScreen.render(bluePlane);
        redMonoScreen.render(redPlane);
        //redMonoScreen.render(bluePlane); --> This throws UnsupportedOperationException*
        redScreenAsScreen.render(redPlane);
        //redScreenAsScreen.render(bluePlane); --> This throws UnsupportedOperationException*
        xMonoScreen.render(new Plane<>(new Color(){})); //--> And still I can define a Monochrome screen as of type Color so  
        System.out.println("Test Finished!");           //still would have a wildcard to make it work as a raw screen (not useful 
                                                        //in my physical model but it is in other abstract models where this problem arises

    } 
}
公共界面屏幕{
公共界面颜色{}
公共类Red实现颜色{}
公共类Blue实现颜色{}
静态屏幕getScreen(){
返回新屏幕(){};
}
默认背景渲染(平面){
返回新背景(plane.getColor());
}
}
单色屏幕.java

public interface Screen {
    public interface Color {}
    public class Red  implements Color {}
    public class Blue implements Color {}

    static Screen getScreen(){
        return new Screen(){};
    }
    default <C extends Color> Background<C> render(Plane<C> plane){
        return new Background<C>(plane.getColor());
    }
}
interface MonochromeScreen<C extends Color> extends Screen{

    static <C extends Color> MonochromeScreen<C> getScreen(final Class<C> colorClass){
        return new MonochromeScreen<C>(){
            @Override public Class<C> getColor() { return colorClass; };
        };
    }

    public Class<C> getColor();

    @Override
    @SuppressWarnings("unchecked")
    default Background<C> render(@SuppressWarnings("rawtypes") Plane plane){
        try{
            C planeColor = (C) this.getColor().cast(plane.getColor());
            return new Background<C>(planeColor);
        } catch (ClassCastException e){
            throw new UnsupportedOperationException("Current screen implementation is based in mono color '" 
                                                   + this.getColor().getSimpleName() + "' but was asked to render a '"
                                                   + plane.getColor().getClass().getSimpleName() + "' colored plane" );
        }
    }
}
public class Plane<C extends Color> {   
    private final C color;
    public Plane(C color) {this.color = color;}
    public C getColor()   {return this.color;}
}
public class Background<C extends Color> {  
    private final C color;
    public Background(C color) {this.color = color;}
    public C getColor()        {return this.color;}
}
public class MainTest<C> {

    public static void main(String[] args) {

        Plane<Red> redPlane   = new Plane<>(new Red());
        Plane<Blue> bluePlane = new Plane<>(new Blue());

        Screen coloredScreen = Screen.getScreen();
        MonochromeScreen<Red> redMonoScreen = MonochromeScreen.getScreen(Red.class);
        MonochromeScreen<Color> xMonoScreen = MonochromeScreen.getScreen(Color.class);

        Screen redScreenAsScreen = (Screen) redMonoScreen;

        coloredScreen.render(redPlane);
        coloredScreen.render(bluePlane);
        redMonoScreen.render(redPlane);
        //redMonoScreen.render(bluePlane); --> This throws UnsupportedOperationException*
        redScreenAsScreen.render(redPlane);
        //redScreenAsScreen.render(bluePlane); --> This throws UnsupportedOperationException*
        xMonoScreen.render(new Plane<>(new Color(){})); //--> And still I can define a Monochrome screen as of type Color so  
        System.out.println("Test Finished!");           //still would have a wildcard to make it work as a raw screen (not useful 
                                                        //in my physical model but it is in other abstract models where this problem arises

    } 
}
界面单色屏幕扩展屏幕{
静态Mon