I have 2 classes e.g. A and B.
These classes have a couple of getter/setter methods with the same name.
Now in the code I do the following:
if(obj.getClassName().equals(A.class.getName())){
A a = (A) obj;
String result = a.getInfo();
}
else if(obj.getClassName().equals(B.class.getName())){
B a = (B) obj;
String result = a.getInfo();
}
I was wondering if there is a way to call the getInfo avoiding the if statements.
Note: I can not refactor the classes to use inheritence or something else.
I was just interested if there is a trick in java to avoid the if statements.
Unless you want to use reflection, no. Java treats two types which happen to declare the same method (getInfo()) as entirely separate, with entirely separate methods.
If you've got commonality, you should be using a common superclass or a common interface that both of them inherit. You've tagged the question "design-patterns" - the pattern is to use the tools that the language provides to show commonality.
As Eng.Fouad shows, using instanceof is simpler anyway - and better, as it means your code will still work with subclasses of A or B.
You can isolate this ugliness, of course, by putting it in a single place - either with a facade class which can be constructed from either an A or a B, or by having a single method which performs this check, and then calling that from multiple places.
If you can't use inheritance and want to avoid if statements (even using instanceof)... well... the best you can do is wrap the check, cast and call in a function to avoid code duplication... otherwise there's no way to do this.
You need reflection. here is my complete example.
Class A
package a;
public class A {
String info;
public String getInfo() {
System.out.println("A getInfo");
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
Class B
package a;
public class B {
String info;
public String getInfo() {
System.out.println("B getInfo");
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
Test Class
package a;
import java.lang.reflect.Method;
public class TestAB {
public static void main(String[] args) {
A a= new A();
doSth(a);
}
private static void doSth(Object obj) {
Class c = obj.getClass();
Method m;
try {
m = c.getMethod("getInfo", new Class[] { });
String result = (String) m.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
See this line :
Class c = obj.getClass();
and
m = c.getMethod("getInfo", new Class[] { });
and
String result = (String) m.invoke(obj);
There is no if statements
If obj is declared as either A or B, you can use overloaded methods. (A good argument for type safety.) Here's a test that illustrates this:
import static org.junit.Assert.*;
import org.junit.Test;
public class FooTest {
class A {
public String getInfo() {
return "A";
}
}
class B {
public String getInfo() {
return "B";
}
}
public String doBarFor(A a) {
return a.getInfo();
}
public String doBarFor(B b) {
return b.getInfo();
}
public String doBarFor(Object obj) {
throw new UnsupportedOperationException();
}
#Test
public void shouldDoBarForA() {
A a = new A();
assertEquals("A", doBarFor(a));
}
#Test
public void shouldDoBarForB() {
B b = new B();
assertEquals("B", doBarFor(b));
}
#Test(expected = UnsupportedOperationException.class)
public void shouldFailIfDeclaredAsObject() {
Object a = new A();
assertEquals("A", doBarFor(a)); // exception thrown
}
}
How about:
String result = null;
if(obj instanceof A)
{
result = ((A) obj).getInfo();
}
else if(obj instanceof B)
{
result = ((B) obj).getInfo();
}
Refer to : this tutorial if this is what you were trying to achieve.
If obj is an Object, you'll need to check. If you don't want to use an if-statement, you can try just casting and catch the exception:
String result = null;
try {
result = ((A)obj).getInfo();
}
catch(ClassCastException e1) {
try {
result = ((B)obj).getInfo();
}
catch(ClassCastException e2) {
// do something else
}
}
Another thing you can do is make both classes implement an Interface then check for just that Interface, something like:
public interface HasInfo
{
public String getInfo();
}
Then add implements HasInfo in the class definition for A and B. Then you can just check (or cast) to HasInfo.
In Java you can use a dot as a scope resolution operator with static methods. Try something like this:
String a_info = A.getInfo();
String b_info = B.getInfo();
With objects, if two interfaces really have the same method with the same parameters and the same return type, why must they be treated differently? Take a look here for some more insight into the problem.
Good luck.
Related
I wanted to know if is it possible to have a method that can choose between multiple classes and return one of them.
This is what it should look like:
public class BotManager {
public static Test test;
public static int PROTOCOL_VERSION = 114;
public Bot bot(){
if(PROTOCOL_VERSION == 114){
return test.bot114;
}else{
return test.bot111;
}
}
in this example, bot111 and bot114 are different classes.
bot.version111.bot.Bot;
bot.version114.bot.Bot;
Yeah of course you can.
You just need to be sure that bot114 and bot111 both extend the Bot class.
For these purposes you would use what's called a factory, here is an example of a factory that returns different Windows based on the Class type:
public static Window getCsvExportWindow(Class tab) {
if (tab == OnhandTab.class) {
return new OnhandCsvExportWindow();
} else {
return new CustOrderCsvExportWindow();
}
}
Instead of Class type you use your PROTOCOL_VERSION.
You can use it's code:
if (Object object instanceof Bot114) {
return new Bot114();
} else if (Object object instanceof Bot111) {
return new Bot111();
} else {
return null;
}
My program is structured as follows: a class that represents an atomic concept which is essentially a String and another class that is made of a list of general concepts. Both classes extends the class Concept that is an abstract class, this means that in the list I could have both atomic concepts and intersection of concepts arbitrary nested.
Each concept, atomic or composed, is printed out by toString method.
Roughly speaking, this is based on this context-free grammar:
C : atom | (C and)+ C
Where C is the abstract class Concept, atom is AtomicConcept and (C and)+ C is Intersection.
This is the AtomicConcept class:
public class AtomicConcept extends Concept{
private String atomicConceptName;
public AtomicConcept(String c) {
this.atomicConceptName = c;
}
#Override
public String toString() {
return atomicConceptName;
}
}
This is che ConceptIntersection class:
import java.util.List;
public class ConceptIntersection extends Concept{
private List<Concept> list;
public ConceptIntersection(List<Concept> l) throws Exception {
if(l.size()>1)
{
this.list = l;
}
else
{
throw new Exception("Intersection needs at least two concepts!");
}
}
public String toString()
{
return Utils.conceptIntersection + Utils.lparen + Utils.splitConcepts(list) + Utils.rparen;
}
}
As you can see in toString function, I also created a method called splitConcepts that takes in input a list of general concepts and returns one string made of each concept separated by comma.
public static String splitConcepts(List<Concept> list)
{
String result = "";
for (Concept item : list) {
System.out.println(item);
result += item.toString() + comma;
}
result = result.substring(0, result.length() - 1);
return result;
}
Where is the problem?
I have trouble with this function because when I call a nested intersection in another one, this function never ends!
One example:
public static void main(String[] args) throws DLRException {
// TODO Auto-generated method stub
AtomicConcept atom = new AtomicConcept("one");
AtomicConcept at = new AtomicConcept("two");
List<Concept> list = new LinkedList<Concept>();
list.add(at);
list.add(atom);
DLRConceptIntersection intersection = new DLRConceptIntersection(list);
System.out.println(intersection); // works fine
list.add(intersection);
DLRConceptIntersection intersection2 = new DLRConceptIntersection(list);
System.out.println(intersection2); //loop never ends!
}
Is a correct approach to fix this problem?
You have a circular reference :
DLRConceptIntersection intersection = new DLRConceptIntersection(list);
list.add(intersection);
This causes the intersection's List to contain a reference to the same instance referred by intersection, which is why toString() run into infinite recursion.
I'm assuming you didn't intend intersection and intersection2 to share the same List.
You can avoid it if you create a copy of the List in the DLRConceptIntersection constructor:
public ConceptIntersection(List<Concept> l) throws Exception {
if(l.size()>1) {
this.list = new ArrayList<>(l);
} else {
throw new Exception("Intersection needs at least two concepts!");
}
}
I don't understand how to use lambdas to pass a method as a parameter.
Considering the following (not compiling) code, how can I complete it to get it work ?
public class DumbTest {
public class Stuff {
public String getA() {
return "a";
}
public String getB() {
return "b";
}
}
public String methodToPassA(Stuff stuff) {
return stuff.getA();
}
public String methodToPassB(Stuff stuff) {
return stuff.getB();
}
//MethodParameter is purely used to be comprehensive, nothing else...
public void operateListWith(List<Stuff> listStuff, MethodParameter method) {
for (Stuff stuff : listStuff) {
System.out.println(method(stuff));
}
}
public DumbTest() {
List<Stuff> listStuff = new ArrayList<>();
listStuff.add(new Stuff());
listStuff.add(new Stuff());
operateListWith(listStuff, methodToPassA);
operateListWith(listStuff, methodToPassB);
}
public static void main(String[] args) {
DumbTest l = new DumbTest();
}
}
Declare your method to accept a parameter of an existing functional interface type which matches your method signature:
public void operateListWith(List<Stuff> listStuff, Function<Stuff, String> method) {
for (Stuff stuff : listStuff) {
System.out.println(method.apply(stuff));
}
}
and call it as such:
operateListWith(listStuff, this::methodToPassA);
As a further insight, you don't need the indirection of methodToPassA:
operateListWith(listStuff, Stuff::getA);
Your MethodParameter should be some interface you define with a single method. This is referred to as a functional interface. You can then pass your methods in. A quick demonstration:
public interface Test{
void methodToPass(string stuff);
}
[...]
public class DumbTest{
//MethodParameter is purely used to be comprehensive, nothing else...
public void operateListWith(List<Stuff> listStuff, Test method) {
for (Stuff stuff : listStuff) {
System.out.println(method(stuff));
}
}
public DumbTest() {
List<Stuff> listStuff = new ArrayList<>();
//fill list
operateListWith(listStuff, methodToPassA);
operateListWith(listStuff, methodToPassB);
}
}
The definition of MethodParameter is missing from your source code. To be used with lambda expressions, it must be a functional interface, for example:
#FunctionalInterface
interface MethodParameter {
String apply(Stuff input);
}
(The #FunctionalInterface annotation is optional.)
To use the method, you have call the method from the interface:
System.out.println(method.apply(stuff));
And thirdly, a method reference always needs a context. In your case you have to do:
operateListWith(listStuff, this::methodToPassA);
operateListWith(listStuff, this::methodToPassB);
You need to use method references.
You don't need to create a method like operateListWith, that's sort of the whole idea. Instead, you can operate on each value using forEach by doing something like this:
listStuff.stream.forEach(object::methodToPassA);
For example:
public class StreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Hello", "What's Up?", "GoodBye");
list.stream().forEach(System.out::println);
}
}
Output:
Hello
What's Up?
GoodBye
In your case, you can get the value inside Stuff using .map, and then operate on it using forEach, like this:
public class DumbTest {
public class Stuff {
public String getA() {
return "a";
}
public String getB() {
return "b";
}
}
public String methodToPassA(Stuff stuff) {
return stuff.getA();
}
public String methodToPassB(Stuff stuff) {
return stuff.getA();
}
public DumbTest() {
List<Stuff> listStuff = Arrays.asList(new Stuff(), new Stuff());
listStuff.stream()
.map(this::methodToPassA)
.forEach(System.out::println);
}
public static void main(String[] args) {
DumbTest l = new DumbTest();
}
}
This is the code of the method that I want to simplify. The method name I call of SerializedExpFamMixture class is exactly the value of "model", my question is how to assign the value of "model" directly as the name of the method instead of using "if" to determine which method I should call. Since by using "if", I need to list all the possible values of "model" and judge which method I should use.
Thank you very much for help. I am new to java.
public static SerializedExpFamMixture RateMtxModel(String model)
{
SerializedExpFamMixture result=new SerializedExpFamMixture();
if(model=="kimura1980()")
result=SerializedExpFamMixture.kimura1980();
if(model=="accordance()")
result=SerializedExpFamMixture.accordance();
if(model=="pair()")
result=SerializedExpFamMixture.pair();
return result;
}
One way you can approach this is to use Reflection:
Method method = myClass.getClass().getMethod("doSomething", null);
method.invoke(myClass, null);
Since you are new to Java, it's time for some general pointers:
In Java, we usually name our methods with camelCase, so the first letter is lower case.
Also, in Java we usually leave the opening curly-bracket on the same line as the code (no newline).
Always use final on your variables. At least your parameters. That way you won't overwrite it, and thus won't have to try to figure out which value it actually has at runtime.
Use curly-brackets! Please!
The result variable is not actually needed.
Use the equals-method to compare Strings.
If you only want one result, use else-if
Fixing these things, your method looks like this:
public static SerializedExpFamMixture rateMtxModel(String model) {
if (model.equals("kimura1980()")) {
return SerializedExpFamMixture.kimura1980();
} else if (model.equals("accordance()")) {
return SerializedExpFamMixture.accordance();
} else if(model.equals("pair()")) {
return SerializedExpFamMixture.pair();
}
return new SerializedExpFamMixture();
}
Next, let's look at what you are actually trying to do here. You want to pass some Strings around, and use them as a basis for creating objects. And now, with the advice given here, you will do this using reflection. This does not sound like a very good idea to me. Say you were to go through with this, and this happened:
rateMtxModel("kinura1980");
Small typo, hard to spot, will give unexpected results. If you were actually calling a method the compiler would let you know that you messed up, now you will get no warning (btw did you see both errors in that method call?). The same if someone were to delete the accordance()-method, the compiler would not alert them that this will break the program.
If it was up to be I would just use the static factory-methods in SerializedExpFamMixture directly, but if you have to do it like this (if the task at hand is using a String input to create an object) I would do something like this:
public enum Something {
KIMURA1980("kimura1980()"),
ACCORDANCE("accordance()"),
PAIR("pair()");
private final String stringValue;
private Something(final String stringValue) {
this.stringValue = stringValue;
}
public static Something fromString(final String string) {
for (final Something something : values()) {
if (something.stringValue.equals(string)) {
return something;
}
}
return null;
}
}
public static SerializedExpFamMixture rateMtxModel(final String model) {
if (model == null) {
throw new IllegalArgumentException("model is null!");
}
final Something something = Something.fromString(model);
if (something == null) {
return new SerializedExpFamMixture();
}
switch(something) {
case KIMURA1980:
return SerializedExpFamMixture.kimura1980();
case ACCORDANCE:
return SerializedExpFamMixture.accordance();
case PAIR:
return SerializedExpFamMixture.pair();
default:
return new SerializedExpFamMixture();
}
}
This way, the one place where you will use the Strings is in the enum, the rest of the code will use the enum constants and thus have the safety of the compiler to rely on.
One could also leave the linking between operation and String to the enum, like this:
interface Operation<T> {
public T run();
}
public enum Something {
KIMURA1980("kimura1980()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.kimura1980();
}
}) ,
ACCORDANCE("accordance()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.accordance();
}
}),
PAIR("pair()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.pair();
}
}),
DEFAULT(null, new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return new SerializedExpFamMixture();
}
});
private final String stringValue;
private final Operation<SerializedExpFamMixture> operation;
private Something(final String stringValue, final Operation<SerializedExpFamMixture> operation) {
this.stringValue = stringValue;
this.operation = operation;
}
public static Something fromString(final String string) {
if (string != null) {
for (final Something something : values()) {
if (string.equals(something.stringValue)) {
return something;
}
}
}
return DEFAULT;
}
public SerializedExpFamMixture getCorrespondingSerializedExpFamMixture() {
return operation.run();
}
}
With this setup in the enum (I think the Operation-part can be trimmed out with Java8), the method will be as simple as:
public static SerializedExpFamMixture rateMtxModel(String model) {
return Something.fromString(model).getCorrespondingSerializedExpFamMixture();
}
Use reflection, but you need to consider a few things:
Bug alert! Comparing Strings using == doesn't work as expected in java - use .equals() instead. However, the solution below bypasses that problem
For the general case, which includes methods not visible to the invoker, you need to consider accessibility, both in finding the method and invoking it
You don't need the result variable, and even if using your code, don't need to initialize it
Try this:
String methodName = model.replace("(", "").replace(")", "");
try {
// getMethod() returns only public methods, getDeclaredMethod() returns any visibility
Method method = SerializedExpFamMixture.class.getDeclaredMethod(methodName);
// if the method is not guaranteed to be visible (eg public) you need this:
method.setAccessible(true);
return (SerializedExpFamMixture) method.invoke(null); // how to invoke on the class object
} catch (Exception forBrevity) {
return new SerializedExpFamMixture();
}
I have a program on my computer that simulates a server on the internet and the fake server needs to be able to send multiple data types to some classes. Like for instance at one point of the program the server needs to send an int to a class then convert that int to a string and send it to another.
Basically what I am asking is if a method can have multiple data types for an input(Does this make sense? if not ill try to explain better). Is there any way to do this without creating many different methods?
Edit: Also is there a way to tell the difference between the types passed in (to prevent errors)
You can have a method which takes Object which is any type. In Java 5.0 and later primitives will be auto-boxed and passed as an object as well.
void method(Object o);
can be called using
method(1);
method("hello world");
method(new MyClass());
method(null);
If I understand correctly, you're asking if a method foo() can have multiple different inputs for its parameters
That way foo(Integer i) and foo(String s) are encased in the same method.
The answer: yes, but it's not pretty
foo(Object o)
Is your method declaration
Now you need to sort out the different types of possibilities
if(o instanceof Integer){
stuff();
} else if (o instanceof String){
moreStuff();
}
Just chain those else/if statements for the desired result.
What you want are Generic methods or classes.
to check what type an object is you'll have to use the 'instanceof' method
you can either make an entire class generic or just a single method, an example of a generic class:
package javahowto;
public class Member<T> {
private T id;
public Member(T id) {
this.id = id;
}
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public static void main(String[] args) {
Member<String> mString = new Member<String>("id1");
mString.setId("id2");
System.out.printf("id after setting id: %s%n", mString.getId());
//output: id after setting id: id2
Member<Integer> mInteger = new Member<Integer>(1);
mInteger.setId(2);
System.out.printf("id after setting id: %d%n", mInteger.getId());
//output: id after setting id: 2
}
Now you now what to look for I'm sure you'll find the best solution to your problem.
check out:
http://download.oracle.com/javase/tutorial/java/generics/index.html
http://en.wikipedia.org/wiki/Generics_in_Java
...
Well I have also wondered and wrote below block. I think instanceof better but I tried getclass.
public static void main(String[] args){
System.out.println(method("This is a test"));
}
private static String method(Object o){
System.out.println(o.toString());
String status = "";
String className;
String[] oList = {"Double","Integer","String","Double","Float","Byte","Short","Long","Character","Boolean" };
for(int i = 0;i<oList.length;i++){
className = "java.lang." + oList[i];
Class testClass;
try {
testClass = Class.forName(className);
if(o.getClass().equals(testClass)){
status = "Your object is " + oList[i];
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return status;
}
You could use the "hashed adapter" pattern.
Public interface Adapter {
Public void handle(object o);
}
Public class StringAdapter implements Adapter {
Public void handle(String st) { // stuff ...
}
Public class IntegerAdapter implements Adapter {
Public void handle(Integer intgr) { // stuff ...
}
Private static final Map adapters = new HashMap();
Adapters.put(string.class, new stringAdapter());
Adapters.put(Integer.class, new IntegerAdapter());
Public void handleMe(Object o) {
Adapters.get(o.getClass()).handle(o);
}
Ive always liked this more than the ol' cascade of ifs and else's.
On my iPad so sorry about formatting and terseness and speellling.