0
votes

This question may be simple (I wish it is not) but i found in an interview. In general, given an interface class IntClass, and a number of classes that implements the interface named ImpIC1, ImpIC2, ..., ImpICk. The methods in IntClass are abstract public methods named void action1(), void action2() ..., void actionM().

I was given a public class Test with public static function named: public static IntClass foo(IntClass c1, IntClass c2). This method do some operations on c1 and c2 to create an an InClass and returns. However, the method must work for every valid implementation of IntClass. The problem is mainly with the definition of c3. How would I design method foo() ..

see below for code details:

public interface IntClass { 
 abstract void action1(); 
 .. 
 abstract void actionM();
 }

..

 public class ImpIC1 implements IntClass { 
 public void action1() { ... } 
 public void action2() { ....}
 ..
 public void actionM() { ... } 
 } 

...

 public class ImpIC2 implements IntClass { 
 public void action1() { ... } 
 public void action2() { ....}
 ..
 public void actionM() { ... } 
 } 

...

 public class ImpICk implements IntClass { 
 public void action1() { ... } 
 public void action2() { ....}
 ..
 public void actionM() { ... } 
 }    

...

public class Test { 
public static IntClass foo(IntClass c1, IntClass c2) { 
 ....
 ...
  return c3; 
}
} 

...

The problem is mainly with the definition of c3. I tried the following solutions (none of which work):

  1. inside foo, c3 was defined as instance of Object .. i.e. IntClass c3 = (IntClass) new Object(). -- compile-time error Object cannot be cast to IntClass
  2. inside foo, c3 was left with no initializion. (compile time error).
  3. inside foo, c3 is null, (null pointer exception)
  4. inside foo, c3 is initialized as one of the implemtation of c3 (i.e. c3 = (IntClass) new ImpIC1()) .. the problem is that if we execute in the exterior ImpIC2 s = (ImpIC1) foo(c1,c2) - we get an error (unable to cast).
5
What is c3 supposed to be? How does it relate to c1 and c2?cheeken
c3 is of type IntClass (which is an interface) and is returned by function foo(IntCLass c1, IntClass c2) ..AJed
I understand you want c3 to be an IntClass. But what is inside c3? How are you combining c1 and c2 to make it? If you simply want to return any IntClass at all, why not do return ImplC1()?cheeken
@Ahmed Jedda - That is not an informative answer to the question what is c3 supposed to be and how it relates to c1 and c2. I still don't see why return null; would not satisfy this problem.emory
Well let's assume that each implementation ImpC1, ImpC2 .. ImpCk uses different data structures but the same functions action1() .. actionM(); then here we want function foo() to work on any implementation .. YET! we define foo() in a public class Test !! - it is really weird and I kept thinkin about it for a day !AJed

5 Answers

1
votes

I don't know about your implementation. But here are some point which we need to know. You can not instantiate interface(Except anonymous inner class). if you want Interface to return change Test Class as follow -

     public class Test { 
            public static IntClass foo(IntClass c1, IntClass c2,String className) { 
 ....
 ...

 IntClass c3 = Class.forName(className);
  //Set properties of c3 as your logic.
  return c3; 

} }

1
votes

Perhaps they wanted to see if you knew how to use reflection to return an instance of whatever class c1 is. Every object is an instance of a class (even anonymous inner classes). You can give an interface for the type of a reference, but in the end it points to a class instance.

public IntClass foo(IntClass c1, IntClass c2) {
    IntClass c3 = c1.getClass().newInstance(); // requires no-arg constructor
    // use c1 and c2 to set up c3 ...
    return c3;
}
1
votes

The posted specifications are extremely vague. "This method do some operations on c1 and c2 to create an an InClass and returns". This does not specify ANY functional requirements at all.

The following solution does some operations on c1 and c2 (they are rather nonsensical operations, but the specifications don't forbid the operations from being nonsensical). It also creates a new IntClass instance which is returned. And, any implementation of an IntClass can be passed as a parameter and the method will still work.

public static IntClass foo(IntClass c1, IntClass c2){
    c1.getClass();
    c2.toString();
    return new IntClass{
            public void action1(){}
            public void action2(){}
            ...
            public void actionM(){}
        };
}

Now, based on my guess on what the requirements might have been, I will present another more sensible solution. This does not fully comply to the stated requirements, but I think it might be what the interviewer was looking for.

public class Test{

    public static IntClass foo(IntClass c1, IntClass c2){
        return new CompositeIntClass(c1, c2);
    }

    public class CompositeIntClass implements IntClass{
        private IntClass c1;
        private IntClass c2;

        public CompositeIntClass(IntClass c1, IntClass c2){
            this.c1 = c1;
            this.c2 = c2;
        }

        public void action1(){
            c1.action1();
            c2.action1();
        }

        public void action2(){
            c1.action2();
            c2.action2();
        }

        ...

        public void actionM(){
            c1.actionM();
            c2.actionM();
        }
    }
}

Using this implementation, calling foo(c1, c2) will formally not perform any operations on c1 or c2. So it doesn't satisfy the stated requirements. However, it will create a new IntClass instance which has the behaviour of c1 and c2 combined. I.e. invoking action2() on c3 will invoke action2() on both c1 and c2. I think this might be what the requirements were supposed to be (just a guess).

So in summary, this means that the returned object (c3) will have the behaviour of any arbitrary IntClass implementations. It will however not mean that you'll be able to do SomeArbitraryImplClass c3 = (SomeArbitraryImplClass) foo(c1, c2);, because doing so would be a violation against the whole concept of interface abstraction (surely nothing that would be advocated in an interview).

The solution is an example of the Composite Pattern.

<EDIT>

If you are using generalized abstractions, you have to ensure that they are compatible with the problems you intend to solve with them. As all the actionI() methods in the described problem setting have a void return type, it is impossible to extract the min and max of any PairInterface instance (as described in your comment). The abstraction does not allow for that.

If that is what you want to do, the solution is much simpler. (This is based on the assumption that the PairInterface contains the declarations int getX() and int getY(), which seems reasonable for a PairInterface):

public class Test{
    public static PairInterface mix(PairInterface c1, PairInterface c2){
        int minX = Math.min(c1.getX(), c2.getX());
        int maxY = Math.max(c1.getY(), c2.getY());
        return new SomePairInterfaceImplementation(minX, maxY);
    }
}

Along with this, you also have to create the SomePairInterfaceImplementation class, or use some existing class which can accept x and y as constructor parameters.

Note that:

1) If PairInterface only declares void methods, it is IMPOSSIBLE to figure out the max or min of x values of c1 and c2. The values are encapsulated and hidden and cannot be accessed.

2) It should not matter what implementation of PairInterface that the method returns. The reason for having an interface return type is that any consistent implementation of the interface should work.

</EDIT>

0
votes

You can create an object of interface by using anonymous class and implement all the abstract methods:

IntClass c3 = new IntClass()
{
    @Override
    public void action1()
    {
        // ...
    }

    // ...

    @Override
    public void actionM()
    {
        // ...
    }

};
0
votes

Are you sure you got the question right? From your comment to Eng.Fouad

ImpClass3 s3 = foo(s1, s2)

It doesn't make sense. It's wrong to have something abstract on the right and something concrete on the left, usually they are vice versa. It's like you order "anything to eat, whatever" but expect exactly "Big Mac Chicken with Cola" :)