2

Let's assume that we have two classes class1 and class2 which are generated classes. These classes are very similar to each other that even some methods are the same, thus I would like to use the same code for them during verification. So, because they are both generated I cannot make an abstract class for them. This is how I do it for now, but I cannot say that I like it.

if (myObject instanceof Class1) {
  if (((Class1) myObject).getId().length() > MAX_LENGTH) {
     throw new Exception();
  }
} else if (myObject instanceof Class2) {
  if (((Class2) myObject).getId().length() > MAX_LENGTH) {
     throw new Exception();
  }
}

Is there a better way?

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
Rufi
  • 2,529
  • 1
  • 20
  • 41
  • 12
    have you considered using `interface`? Put the shared method `getId` in the `interface` – Ian May 20 '16 at 08:17
  • When you say they are generated, how or what is generating them? Unless you can use either an `abstract` or `interface`, you can't assume they will have the same methods – Draken May 20 '16 at 08:23
  • If you can't use an interface either, you could hack something with introspection. It should really be a last resort though – Aaron May 20 '16 at 08:23
  • Is Scala an option? Then you could use structural typing or implicit conversions and type classes. – deamon May 20 '16 at 08:28
  • 1
    If you can't modify the class implementations maybe [invoking the method's by reflection](http://stackoverflow.com/a/2407242/6242846) is an option. – Mad Matts May 20 '16 at 08:38
  • About the generation. I am using YaaS SDK or YaaS platform which offers the generation from json schemas to java classes. I haven't found the information that it is possible there to use some inheritance. – Rufi May 20 '16 at 11:10

6 Answers6

2

A slightly better version in my opinion:

String id = null;
if (myObject instanceof Class1) {
  id = ((Class1) myObject).getId();
} else if (myObject instanceof Class2) {
  id = ((Class2) myObject).getId();
}
if (id == null || id.length() > MAX_LENGTH) {
  throw new Exception();
}

But that's just my take on your concrete example, not a solution for your general problem. You could always use reflection if you know that the method will be there.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
Kevin Wittek
  • 1,369
  • 9
  • 26
2

I think that the adapter pattern could be an idea (might not be really this pattern but the idea came from him).

This might be a lot of work just for you example but for more complex work, this is a good approch (personnal opinion of course).

Create an interface an two class that will adapt these function like this :

The originals classes

Class A

public class A {

private int id;

    public A(int id){
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

And the class B

public class B {

    private int id;

    public B(int id){
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

Create an interface like :

public interface Adapter {

    public int getId();
}

And create both adapters :

Adapter A :

public class AdapterA implements Adapter{

    private A a;

    public AdapterA(A a){
        this.a = a;
    }

    public int getId() {
        return a.getId();
    }
}

and Adapter B

public class AdapterB implements Adapter{

    private B b;

    public AdapterB(B b){
        this.b = b;
    }

    public int getId() {
        return b.getId();
    }
}

Now if you need to work with this class, just create an adapter and call the getId of these adapter.

Using your example (Class1 = A and Class2 = B)

  Adapter adapter = new AdapterA(a); //This mean that you need to create the Adapter when you create (receive the class A or B)
  //Adapter adapter = new AdapterB(b);

  if (adapter.getId().length() > MAX_LENGTH) {
     throw new Exception();
  }

If one method have a different name, you just need to update the getter in the specific adapter, the logic behind won't change. So if in B the getId become getIdentity, the only update would be in the adapterB.

AxelH
  • 14,325
  • 2
  • 25
  • 55
1

As I commented, one way to do this would be to have shared method getId in the interface

public interface ClassInterface
{
    public String getId ();
}

public class Class1 implements ClassInterface{
    public String getId (){
        //class 1 implementation
    }
}

public class Class2 implements ClassInterface{
    public String getId (){
        //class 2 implementation
    }
}
Ian
  • 30,182
  • 19
  • 69
  • 107
  • I'm not sure if the questioner can use this aproach, since it seem's he does not have any control over the class definiton? – Kevin Wittek May 20 '16 at 08:26
  • I don't feel that there is any difference between this and abstract class. As I mentioned Class1 and Class2 are generated and I am not able to add there anything except fields with getters and setters. – Rufi May 20 '16 at 11:12
  • 1
    @VladimirTikhomirov interface is better in this case because you can have more than one interface. Just saying ;) – AxelH May 20 '16 at 12:34
1

Supposing you can't modify at all the generated classes (which seems strange), here is how I would do it with introspection :

Class clazz = myObject.getClass();
Method getId = clazz.getMethod("getId");
Object result = m.invoke(myObject);
if (((String)result).length() > MAX_LENGTH) {
    throw new Exception();
}

Note that this suppose your class has an unique getId() method with no argument. If that's not the case, you'd have to push it a little further and check the methods signature.

Also, I don't think it is a better way than your current sample code. I would maybe use it if the real code had 5+ different classes, or if you needed to do 5+ similar operations on them.

Aaron
  • 24,009
  • 2
  • 33
  • 57
  • 2
    You should probably use `myObject.getClass().getMethod("getId")` instead of iterating all methods. It will get the method without any parameters. – kapex May 20 '16 at 08:35
  • Yeah, that looks easier indeed. I hadn't used that in a while and followed the reflection Trail, but unless you have to check signatures that looks overkill. I'm editing my answer. – Aaron May 20 '16 at 08:50
1

You can also try to use reflection to get common methods by their signature if you claim them to be the same. It would go something like this:

if (o instanceof Class1 || o instanceof Class2) {
    Class classDescr = o.getClass();
    Method m = classDescr.getMethod("method");
    m.invoke(o);
}

If both classes have the method method with no arguments it would work just fine. Without taking some reflection overhead into account of course.

Aleksandr Erokhin
  • 1,904
  • 3
  • 17
  • 32
0

Depending on the abilities of the code generation tool, you could use a common interface. Maybe you could even let the tool create common classes that are used inside the other classes (delegation).

deamon
  • 89,107
  • 111
  • 320
  • 448