在Java中处理具体类型时的替代方法

时间:2014-05-23 12:58:18

标签: java inheritance instanceof

我们说我有以下类层次结构:

public interface Result {
}
public class Failure implements Result {
    private String msg;
    public Failure(String msg) {
        this.msg = msg;
    }
    @Override public String toString() { return msg; }
}
public class Success implements Result {
    private int time, count;
    public Success(int time, int count) { this.time = time; this.count = count; }
    public int getTime() { return time; }
    public int getCount() { return count; }
    @Override public String toString() { return time + ", " + count; }
}

基本上定义了一个具有两个具体类的接口Result:Success和Failure。

然后,我有一个计算方法返回一个结果,并根据结果我应该做些什么,例如:

Result result = calculate();

我有几个选项来弄清楚具体的课程。最明显的一个是使用实例:

if (result instanceof Success) {
    int time = result.getTime();
    int count = result.getCount();
    // Do something with them
} else {
    throw new RuntimeException(result.toString());
}

在支持模式匹配的Scala等编程语言中,我可以使用像

这样的匹配
case Success =>
case Failure =>

但由于Java不支持它,我不确定最好做什么。我可以按如下方式扩展Result接口:

public interface Result {
    public boolean isSuccess();
    public boolean isFailure();
    public Success asSuccess();
    public Failure asFailure();
}

然后将客户端代码写为:

if (result.isSuccess()) {
   Success success = result.asSuccess();
   int time = success.getTime();
   int count = success.getCount();
   // Do something with them
} else {
   throw new RuntimeException(result.toString());
}

但在这种情况下,接口绑定到具体的类,这也是不好的。

表示此类案件的最佳方式是什么?我真的不想把处理代码放在Result层次结构中,因为它实际上取决于客户端获取数据并做任何他们想做的事情,因此多态调度解决方案可能不是最好的。结果层次结构充其量只是一个 数据类型封装,而不是操作。

7 个答案:

答案 0 :(得分:2)

IMO,这是" Marker Interface"图案。您确实想要使用界面定义的某些功能,然后不要将其作为标记。添加方法。为了使其具有通用性,您可能想知道Result的类型/类别,例如Success, Error, Fatal, Warning, etc...,您可以考虑在您的界面中创建枚举enum ResultType,除了对于功能方法(getTime, getCount...),添加getType()或只使用abstract class Result并声明protected Type type

要确定具体类型并执行相应的操作,您可以考虑在这些枚举类型上使用case语句。但是你不必将它们cast变为具体类型,因为你想要的所有功能都在接口/抽象类Result中定义。

抱歉缺少代码示例。现在不方便编码。

答案 1 :(得分:1)

如果不仅有两种子类型,那么访问者模式将是合适的。考虑:

interface Result {
    // resut of the implementation
    public <T> T accept(Visitor<T> visitor);
}

class Success implements Result {
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

class Failure implements Result {
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

然后创建一个访客类:

interface Visitor<T> {
    T visit(Failure failure);
    T visit(Success success);
}

然后你将使用instanceof

Result result = ...;
// this could be something else, with a different template parameter.
String visitorResult = result.accept(new Visitor<String>() {
    @Override
    public String visit(Failure failure) {
        return "Failure visited";
    }

    @Override
    public String visit(Success success) {
        return "Success visited";
    }
});

这只是我对实现的想法,通过泛型,您可以添加更多可自定义的功能。如果您想了解更多相关信息:http://en.wikipedia.org/wiki/Visitor_pattern

答案 2 :(得分:0)

这可以简化为

public interface Result {
  public boolean isSuccess();
  public Integer getTime();
  public Integer getCount();
}

你必须在失败时实现计数和时间(返回零/ null或抛出异常),然后:

if (result.isSuccess()) {
  int time = result.getTime();
  int count = result.getCount();
  // Do something with them
} 
else {
  throw new RuntimeException(result.toString());
}

你不需要在界面上使用字符串,但是你可能想要明确地告诉别人开发者担心将来是否会有另一个结果(BigSuccess,MssvieFail等)。

结果上的布尔值,表示成功似乎简单直观。

答案 3 :(得分:0)

此处需要注意的一点是,如果result.isSuccess()在您的代码中返回true,则您无需使用asSuccess()方法将其强制转换为Success,你可以简单地使用类型转换(Success) result而不用担心会引发运行时异常。

我认为instanceof并不是什么大问题,但如果你仍想要一个解决方法,我想的可能就是这样:

public interface Result {
    public String getType();
}

public class Success implements Result {
    public static final String TYPE = "Success";

    //..... Some class relevant code here

    public String getType() {
        return TYPE;
    }
}

public class Failure implements Result {
    public static final String TYPE = "Failure";

    //..... Some class relevant code here

    public String getType() {
        return TYPE;
    }
}

然后,如果您使用的是JDK版本7或更高版本 支持带有字符串的 的开关语句),您可以尝试以下操作:

switch(result.getType()) {
    case Success.TYPE:
         //Success code here
    case Failure.TYPE:
         //Failure code here
}

我没有对此进行测试,但我认为它可以满足您的需求。

我希望我能帮到你!

答案 4 :(得分:0)

界面中的其他字段不会真正限制该界面的“可用性”。 Normaly接口旨在被具体类使用。

在您的情况下,我更喜欢处理“成功”和“失败”的通用Result类。

答案 5 :(得分:0)

还有一个选项:访客模式:

public interface ResultVisitor {
    void handleSuccess(Success result);
    void handleFailure(Failure result);
}

public interface Result {
    void accept(ResultVisitor visitor);
}
public class Failure implements Result {
    private String msg;
    public Failure(String msg) {
        this.msg = msg;
    }
    @Override public String toString() { return msg; }
    @Override void accept(ResultVisitor visitor) {visitor.visit(this);}
}
public class Success implements Result {
    private int time, count;
    public Success(int time, int count) { this.time = time; this.count = count; }
    public int getTime() { return time; }
    public int getCount() { return count; }
    @Override public String toString() { return time + ", " + count; }
    @Override void accept(ResultVisitor visitor) {visitor.visit(this);}
}

然后,代码中的某处:

result.accept(new ResultVisitor() {
    void handleSuccess(Success success) {
        // do something with success
    }
    void handleFailure(Failure failure) {
        // do something with failure
    }
});

答案 6 :(得分:0)

  

但在这种情况下,接口绑定到具体的类,这也是不好的。

如果这是您关注的问题,并且您希望获得成功和失败的其他实施,那么只需创建成功和失败接口。

所有对象都有一个toString(),因此您可能不需要失败。