从接口和抽象类继承类时导致问题的异常规范

时间:2017-07-10 15:38:29

标签: java exception exception-handling

interface A{
    void f() throws FillInStack;
}
abstract class Abs{
    abstract public void f() throws MyExc;
}
class interface_excep extends Abs implements A{
    @Override
    public void f(){   }  
}

上面的代码编译没有错误。 FillInStack MyExc 是源自例外

的简单类

我甚至在两个不同的异常情况下使用了函数f(),没有出现错误......

第一案:

A a=new interface_excep();
try{
    //...
    a.f();
}
catch(FillInStack m){ 
    //...
}

第二案:

Abs a=new interface_excep();
try{
    //...
    a.f();
}
catch(MyExc m){ 
    //...
}

但是当我试图从函数 f()中抛出异常时,它会给出错误。

public void f() throws MyExc{ //! Error
    //...
    throw new MyExc();
    //...
}

我知道从接口A和抽象类Abs中更改方法f()的名称将解决问题。但是,如果我从某个供应商库提供给我的接口和抽象类中导出我的类并进入类似的情况,那么我将如何从该函数中抛出任何异常呢?

Java是否有任何隐含的解决方案,或者只是我们必须有两个具有不同名称的函数???

修改

以上两个例外的代码......

class MyExc extends Exception{  }
class FillInStack extends Exception{   }

2 个答案:

答案 0 :(得分:1)

因为library(compositions); library(rgl) TimeSeries <- cbind(runif(10),runif(10),runif(10),runif(10)) TimeSeries <- TimeSeries/rowSums(TimeSeries) Acomp <- acomp(TimeSeries) plot3D(Acomp, cex=10, col="red", log=FALSE, coors=T, bbox=F, scale=F, center=F, axis.col=1, axes=TRUE) ids <- rgl.ids() pts <- ids$id[ids$type == "points"] lines3d(rgl.attrib(pts, "vertices")) 有两个不同的签名,所以你在疯狂地试图继承这两个版本。当您声明f(不合规类型名称!)时,您告诉编译器A a=new interface_excep();类型为a,这就是为什么它识别A中的异常抛出的原因。与catch(非兼容类型名称!)类似,您告诉编译器Abs a=new interface_excep();可以抛出另一个异常。但是,当您尝试在继承apublic void f() throws MyExc的类中定义A时,您需要添加Abs无法识别的限制,这违反了超越规定。所以你不能这样做,也不能覆盖它以抛出A,因为这违反了FillInStack的合同。因此,您可以(实际上必须)捕获任一异常,具体取决于变量的编译时类型,但您永远不能抛出任何异常。

您有意设置了不兼容的合同,因此没有实施类可以同时满足这两者。所以不,你不会侥幸逃脱。

根据Liskov替换原则,任何子类型必须适合其超类型。这意味着覆盖可以声明更少的Abs异常,但绝不会超过其超类型。因此,您需要修复界面以允许两个异常,或者忘记它。

答案 1 :(得分:0)

不要让AAbs都有一个名为f的方法让你误以为它们是同一种方法。由于Abs未实现A,因此它们是两个完全不同的方法,巧合地具有相同的名称。一旦你意识到这一点,那么这很容易。我假设您在询问,因为您希望使用Abs来实施A并且您无法修改。您可以使用合成和适配器模式,而不是使用继承。由于Abs是抽象的,我们只需要添加另一个图层:

public class Concrete extends Abs {
   // Implement any abstract methods from Abs here
}

public AbsToAAdapter implements A {
   private final Abs abs;

   public AbsToAAdapter(Abs abs){
      this.abs = abs;
   }

   // Override f from interface A
   @Override
   public void f() throws FillInStack {
      try {
         abs.f();
      } catch (MyExc ex){
         throw new FillInStack(ex); // Assuming FillInStack has constructor that takes a Throwable as its cause
      }
   }
}

public class ClassThatUsesA {

   public static void main(String[] args){
      A a = new AbsToAAdapter(new Concrete());
      try {
        a.f();
      } catch (FillInStack ex){
         // Handle ex
      }
   }
}

同样,这假设您无法控制任何相关课程。控制类时,您确实有更多选项。也就是说,Abs可以扩展AMyExc可以扩展FillInStack

这可能看起来很多,但如果这两种方法有不同的名称,那么你就不会对这样的方法三思而后行。它也是唯一的选择,因为Abs.fA.f之间没有任何关系。