【问题标题】:Accepting different types of arguments in Java在 Java 中接受不同类型的参数
【发布时间】:2013-10-11 21:16:36
【问题描述】:

这是一个我不确定如何用 Java 解决的问题。我想根据三种类型的数据(URI、String 或 Literal)制作三重语句,每种类型的编码方式都不同。我已经编写了接受这些类型的编码方法。

public static String makeStatement(URI subject, URI predicate, String object) {
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}

public static String makeStatement(String subject, URI predicate, String object) {
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}

public static String makeStatement(URI subject, URI predicate, Literal object) {
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}

private static String encode(String binding) {
    return "?" + binding;
}

private static String encode(URI uri) {
    return "<" + uri.stringValue() + ">";
}

private static String encode(Literal literal) {
    return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
}

但由于我可以接受这些类型的任何组合,这将需要 9 个 makeStatement 函数,它们基本上做同样的事情,这似乎是个坏主意,特别是因为我以后可能想添加另一种类型。

通常我会用创建超类的建议来回答这样的问题,但我无法编辑字符串、URI 和文字。另一种选择是定义

public static String makeStatement(Object subject, Object predicate, Object object) {
    String encodedSubject = "", encodedPredicate = "", encodedObject = "";
    if (subject.getClass().equals(URI.class)) {
        encodedSubject = encode((URI) subject);
}
    return " " + encode(encodedSubject) + " " + encode(encodedPredicate) + " " + encode(encodedObject) + ".\n";
}

然后检查每个参数的类,但我认为这不是很优雅。 另一个建议是定义诸如 makeStatement(URI subjectURI, String subjectString, Literal subjectLiteral, URI predicateURI.. etc) 之类的内容,然后检查哪些参数为空并从那里开始,但这意味着当我调用功能。 第三个选项是https://stackoverflow.com/a/12436592/1014666,但在调用 makeStatement 函数时,这同样需要一些额外的输入。

有什么建议吗?

【问题讨论】:

  • 一位同事不久前遇到了类似的问题,他编写了一个 Python 脚本,将所有 9 种组合作为文本写入 .java 文件中
  • 不错,但不是很优雅:)
  • 编写一个通用方法来接受所有东西作为一个对象,并在其中执行检查实例并根据需要执行操作。它可能会给您其他视角。
  • @Mayilarun 我已经在我的问题中提到了这种可能性。
  • Masoom Raza 提出的建议并不是一个好的做法,因为带有签名 makeStatement(Object, Object, Object) 的方法会阻止所有编译时类型检查。当然,你可以传入 URI、String 或 Literal 的任意组合,但你也可以传入 Date、BigDecimal、Socket 等等。

标签: java overloading


【解决方案1】:

您可以使用构建器模式:

    public class StatementMaker {
    private static String encode(String binding) {
        return "?" + binding;
    }

    private static String encode(URI uri) {
        return "<" + uri.stringValue() + ">";
    }

    private static String encode(Literal literal) {
        return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
    }

    public static Statement from(String b) {
        return new Statement(encode(b));
    }

    public static Statement from(URI b) {
        return new Statement(encode(b));
    }

    public static Statement from(Literal b) {
        return new Statement(encode(b));
    }

    public static class Statement {

        private StringBuilder buf;
        private Statement(String s) {
            buf = new StringBuilder(" ");
            buf.append(s);
        }

        public Statement with(String s) {
            buf.append(" ").append(encode(b));
            return this;
        }

        public Statement with(URI s) {
            buf.append(" ").append(encode(b));
            return this;
        }

        public Statement with(Literal s) {
            buf.append(" ").append(encode(b));
            return this;
        }

        public String toString() {
            return buf.toString() + ".\n";
        }

    }
}

您现在可以这样构造语句:

StatementMaker.from(subject).with(predicate).with(object).toString()

在需要语句的代码中,您可以使用静态导入进一步缩短代码:

import static my.package.StatementMaker.from;

那么语句就简化为:

from(subject).with(predicate).with(object).toString()

您可以向内部类添加另外 3 个方法:

public static class Statement {

    private StringBuilder buf;
    private Statement(String s) {
        buf = new StringBuilder(" ");
        buf.append(s);
    }

    public Statement with(String s) {
        buf.append(" ").append(encode(b));
        return this;
    }

    public Statement with(URI s) {
        buf.append(" ").append(encode(b));
        return this;
    }

    public Statement with(Literal s) {
        buf.append(" ").append(encode(b));
        return this;
    }

    public String and(String s) {
        buf.append(" ").append(encode(b));
        return buf.toString() + ".\n";
    }

    public String and(URI s) {
        buf.append(" ").append(encode(b));
        return buf.toString() + ".\n";
    }

    public String and(Literal s) {
        buf.append(" ").append(encode(b));
        return buf.toString() + ".\n";
    }


    public String toString() {
        return buf.toString() + ".\n";
    }

}

然后您可以像这样使用避免toString() 调用:

String statement = from(subject).with(predicate).and(object);

【讨论】:

  • 我喜欢这个答案,因为 makeStatement 变得更加清晰,但是,每行末尾的 toString 不太好。
  • 在此之后您甚至不需要 makeStatement 方法。如果你想避免可以向 Statement 内部类添加 3 个方法。我会添加他们来回答。
  • 添加了一种避免 toString() 调用的方法。
  • 现在我最喜欢这个了 :) 谢谢
【解决方案2】:

如果只有几个选项,方法重载效果很好。你在这里的东西有点强迫症。如果有一种从一种转换到另一种的简单方法,您就不需要拥有所有选项。

因此,忘记所有可能的选择,并提供最常用的选择。

【讨论】:

  • 不幸的是,没有简单的方法可以从一种转换为另一种。对于我目前正在开发的软件,我发现自己正在编写函数,其中 80% 的行可能由 makeStatement 调用组成。
  • 没有简单的方法将 URI 转换为字符串,反之亦然?
  • 当然有一种简单的方法可以将 URI 转换为字符串,但在我的上下文中,我想用 包装一个 URI 并放一个 ?在一个字符串之前。
【解决方案3】:

通常我会用创建超类的建议来回答这样的问题,但我不能 编辑字符串、URI 和文字。另一种选择是定义

我会采用类似的方法,但不是提取超类,正如你所说,你不能这样做,你可以创建一个包装器。

public class LiteralWrapper {
    private String string = null;
    private URI uri = null;
    private Literal literal = null;

    public LiteralWrapper(String sting) {
        this.string = string;
    }

    public LiteralWrapper(URI uri) {
        this.uri = uri;
    }

    public LiteralWrapper(Literal literal) {
        this.literal = literal;
    }

    // Note that this class is immutable, 
    // so you know you cannot have more than one non-null member. 
    // Probably not a bad idea to add some getters, though.


    /* The encode functions from your original question */

    private static String encode(String binding) {
        return "?" + binding;
    }

    private static String encode(URI uri) {
        return "<" + uri.stringValue() + ">";
    }

    private static String encode(Literal literal) {
        return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
    }

    @Override
    public String toString() {
        if (literal != literal) {
            return encode(literal);
        }
        if (uri != null) {
            return encode(uri);
        }
        return encode(string);
    }
}

现在您的makeStatement 代码变得微不足道:

public static String makeStatement(LiteralWrapper subject, LiteralWrapper predicate, LiteralWrapper object) {
    return " " + subject + " " + predicate + " " + object + ".\n";
}

编辑:
根据下面的评论,这使得调用 makeStatement 有点烦人。而不是能够做makeStatement(myString, myUri, myLiteral),你不得不打电话给makeStatement(new LiteralWrapper(myString), new LiteralWrapper(myUri), new LiteralWrapper(myLiteral))。 这个问题不能用给定的解决方案完全避免,但可以通过以工厂方法的形式引入一些语法糖来缓解:

public static LiteralWrapper wrap(String string) {
    return new LiteralWrapper(string);
}

public static LiteralWrapper wrap(URI uri) {
    return new LiteralWrapper(uri);
}

public static LiteralWrapper wrap(Literal literal) {
    return new LiteralWrapper(literal);
}

现在,您可以在需要使用makeStatement 的任何地方静态导入这些包装器:

import static org.some.package.LiteralWrapper.wrap;
/* other imports*/

public class MyClass {
    public void someFunction() {
        /* some business logic */

        makeStatemet(wrap(myString), wrap(myURI), wrap(myLiteral));
    }
}

【讨论】:

  • 感谢您的回答,这与我在问题中提到的答案相似。到目前为止,这是最优雅的选择,但是这会将我的 makeStatement("subject", "predicate", "object") 变成 makeStatement(new StatementWrapper("subject"), new StatementWrapper("predicate"), StatementWrapper("目的”));这并没有真正提高可读性。
  • @Rhand 我同意这有点妨碍可读性。请参阅我对上述答案的编辑,以了解缓解问题的方法。恕我直言,该解决方案的代码可读性很强,但这一切都在旁观者的眼中。
  • 我喜欢换行功能(这是由其他一些答案扩展的)
  • 感谢您的回答,这可行,但我更喜欢 U Mad 的回答
【解决方案4】:
public static String makeStatement(Object subject, Object predicate, Object object) {
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}

private static String encode(Object obj) {
   String  encodedOj ="";
   if (obj.getClass().equals(URI.class)) {
        encodedOj = encode((URI) obj);
   }else if(obj.getClass().equals(Literal.class)){
      encodedOj = encode((Literal) obj);
   }else if(obj.getClass().equals(String.class)){
      encodedOj = encode((String) obj);
   } 
   return encodedOj;
}

private static String encode(String binding) {
    return "?" + binding;
}

private static String encode(URI uri) {
    return "<" + uri.stringValue() + ">";
}

private static String encode(Literal literal) {
    return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
}

【讨论】:

  • 你知道检查每个 Object obj 的类是否会影响性能吗?与 U Mad 的回答相比,这似乎是主要区别(并且 makeStatement 函数非常不同)。
  • 感谢您的回答,这可行,但我更喜欢 U Mad 的回答
  • 问题是可以传入任何对象。这本身就是代码异味,但更糟糕的是,如果调用makeStatement 时使用的不是URILiteralStringencode(Object) 方法静默失败并返回一个空字符串。您至少应该在 if 块中添加 else throw new IllegalArgumentException
【解决方案5】:

您可以使用静态工厂方法(参见Effective Java,第 1 条)和匿名类(充当类似于闭包的东西)创建有趣且易于使用的包装器。

方法如下:

public class Item {

    private static interface Methods {
        public String encode();
    }

    private final Methods methods;

    private Item(Methods methods) {
        this.methods = methods;
    }

    public static Item of(final String binding) {
        return new Item(new Methods() {
            @Override
            public String encode() {
                return "?" + binding;
            }
        });
    }

    public static Item of(final URI uri) {
        return new Item(new Methods() {
            @Override
            public String encode() {
                return "<" + uri.stringValue() + ">";
            }
        });
    }

    public static Item of(final Literal literal) {
        return new Item(new Methods() {
            @Override
            public String encode() {
                return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
            }
        });
    }

    public String encode() {
        return methods.encode();
    }
}

使用这种方法添加新支持的类型(这是您的要求之一)非常容易:只需创建接受此类型的新静态工厂方法:Item.of(NewType val)

所以你会有一个方法:

public static String makeStatement(Item subject, Item predicate, Item object) {
    return subject.encode() + " " + predicate.encode() + " " + object.encode();
}

然后这样称呼它:

makeStatement(Item.of(subject), Item.of(predicate), Item.of(object));

添加新方法也很容易(但它有点像修改而不是扩展)——只需将它们添加到Methods 接口并在闭包中实现所有支持的类型。无论如何,编译器会让你这样做。

【讨论】:

  • 感谢您的回答,这可行,但我更喜欢 U Mad 的回答
  • 确实,它更适合您的特定问题。我试图解决一些更一般的问题(接受不同类型的论点)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多