Accepting various types of arguments in Java

This is a question that I'm not sure how to solve the problem in Java. I want to make triple statements based on three data types, URI, String or Literal, each type is encoded differently. I wrote coding methods that accept these types.

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(); } 

But since I can accept any combination of these types, this will require 9 makeStatement functions that basically do the same thing, and this seems like a bad idea, especially since I might want to add another type later.

Normally I would answer such a question with a proposal to create a superclass, but I can’t edit String, URI and Literal. Another option would be to define

 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"; } 

and then check the classes for each argument, but I find it not very elegant. Another suggestion is to define something like makeStatement (URI subjectURI, String subjectString, Literal subjectLiteral, URI predicateURI .. etc.), and then check which arguments are null and go from there, but that would mean typing a lot of zeros. when i call the function. The third option would be https://stackoverflow.com/a/412960/2129 , but again this will require quite some additional input when calling the makeStatement function.

Any suggestions?

+7
java method-overloading
source share
5 answers

You can use the builder pattern:

  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"; } } } 

Now you can build the instruction as such:

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

In code that needs operators, you can shorten code with static import:

import static my.package.StatementMaker.from;

Then the statement boils down to:

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

You can add 3 more methods to the inner class:

 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"; } } 

Then you can use the toString() call like this:

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

+3
source share

Method overloading works great if there are only a few options. What you have here is a bit intrusive. You do not need to have all the options if there is an easy way to convert from one to another.

So forget about every possible choice and make the most affordable.

+2
source share

Normally, I would answer such a question with a proposal to create a superclass, but I cannot edit String, URI and Literal. Another option would be to define

I would go for a similar approach, but instead of fetching a superclass that you said you cannot do, you can create a wrapper.

 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); } } 

Now your makeStatement code becomes trivial:

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

EDIT:
According to the comment below, calling makeStatement bit annoying. Instead of doing makeStatement(myString, myUri, myLiteral) , you can call makeStatement(new LiteralWrapper(myString), new LiteralWrapper(myUri), new LiteralWrapper(myLiteral)) . This problem cannot be completely eliminated with this solution, but it can be mitigated by introducing some syntactic sugar in the form of factory methods:

 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); } 

Now you can statically import these wrappers wherever you need to use 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)); } } 
+1
source share
 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(); } 
+1
source share

You can create a fun and convenient shell using static factory methods (see Effective Java, paragraph 1) and anonymous classes (acting as something like closure).

Here's how:

 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(); } } 

With this approach, it is very easy to add new supported types (which were one of your requirements): just create a new static factory method accepting this type: Item.of(NewType val) .

So you will have a method:

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

And call it that:

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

Adding new methods is also quite simple (but it seems more like a modification instead of an extension) - just add them to the Methods interface and implement them in a close for all supported types. Anyway, the compiler will make you.

0
source share

All Articles