views:

277

answers:

5

Let me describe the situation, and I'm sure I'm just thinking about this problem incorrectly. I have a concrete class that will implement an interface. I want to enforce in the contract that the class must have a constructor with a specific type. So for instance:

interface MyInterface {}
public class MyClass implements MyInterface {
    public MyClass(HashMap<String, String> params) {

    }
}

I want to ensure that MyClass is instantiated with a single HashMap argument, which seems like it would be done something like this:

interface MyInterface<T>

Other than using generics on method signatures, I've never used them with classes or interfaces, and I'm really a beginner with that, so please explain any generics involved with the solution... or the alternative solution if I'm thinking about this incorrectly (architecturally speaking). Thanks!

A: 

Instead of an Interface you would have to create an abstract class, declare the constructor in there and then any class which inherits from it would have to call the base constructor. May be make the default constructor inside the base class private also.

Andrew

REA_ANDREW
The derived class constructor would be forced to call the constructor in the abstract superclass, but this still does not force the derived class to have a constructor with specific argument types.
Jesper
+3  A: 

You can't enforce a contract on constructor using interface in Java. The best approximation you could get is by defining an interface for a factory, with a create() method that takes a single HashMap...

Also you can replace the interface with an abstract class for which the constructor requires an HashMap, that will force sub classes to give one, but not more (the sub classes will not necessarily have an HashMap parameter).

penpen
So it seems like the factory is the only way to go because you can't enforce a specific type even through extending an abstract class. Is that a correct assumption?
hal10001
@hal10001: Correct, you have absolutely no control over the constructor signatures of subclasses or implementing classes. (Most of the time, that's a **good** thing, but if you have a speciic requirement and you're *really sure* you need to control this aspect of the concrete class, you'll need a factory.)
T.J. Crowder
"if you have a speciic requirement": regarding this, could you tell us more on what you want to do? For instance, could it be for creating new instances using reflection?
penpen
Solution: I have a concrete manager class in which I do not provide a no-arg constructor. The arg constructor takes a HashMap, and then uses a setter and setter property provided on the parent abstract class to set that HashMap. If another developer wants to extend the abstract class and provide a different implementation, then they can do that. The abstract class implements an interface that defines my contract requirements.
hal10001
A: 

It is not possible in Java to force a class to implement a constructor with specific argument types. Why would you want to do this? Normally, it isn't useful to do this.

You can force a non-abstract class to implement a certain method by making the class implement an interface or extend an abstract class in which that method is declared (but not defined). That's useful because of polymorphism; you can make a variable of the superclass type refer to an instance of the subclass, and call the method:

interface Animal {
    void makeSound(); }
}

class Dog implements Animal {
    void makeSound() {
        System.out.println("Woof!");
    }
}

// Later:

Animal a = new Dog();
a.makeSound();

Now you can call makeSound() without knowing that a refers to a Dog; you know that the object that a refers to has a makeSound() method, because it's declared in the interface Animal (and a is an Animal).

In the case of constructors, you never call a constructor polymorphically; you always call it by mentioning the name of the concrete class that you want to instantiate. Because of that, it's not very useful to force a class to have a constructor with certain parameters.

The only case where this might be useful is when instantiating classes dynamically via reflection. Through reflection you can find out which constructors a class has, and call one of them dynamically. You could then thrown an exception if the class doesn't have the specific constructor that you need - but there's no way to check this at compile-time.

Jesper
It could be useful if he is using reflection to instantiate types.
tster
+2  A: 

Adding to the above answers, a constructor is an implementation detail. An interface defines a contract which describes how it behaves, not how it is assembled.

For this reason, you can't force an implementor to have a specific constructor. I think that whatever you're trying to do, an interface is not the way.


Update: This Annotation Processing Tool article describes how you can create an annotation and annotation processor which take effect at compile time and validate that a class has a no-arg constructor.

It can be adapted to work for a 1-argument constructor which takes a map instance.

To quote the article, its usage would be similar to:

@NoArgsConstructor
public abstract class NoArgsSuperClass {
  public NoArgsSuperClass() {
  }
}

// Passes
public class PublicNoArgsConstructor extends NoArgsSuperClass {
  public PublicNoArgsConstructor() {
  }
}

// Fails
public class NonPublicConstructor extends NoArgsSuperClass {
  NonPublicConstructor() {
  }
}
Robert Munteanu
I don't really agree with you that the construction of an object is an implementation detail. It is useful to be able to say that "this object must have these capabilities, and it will act in this universe" - where the capabilities are the methods, and the "universe" is whatever you send the constructor. The omnipresent `init` method which most plugin architectures have (like `Servlet.init(ServletConfig)`) is just a kludge to get around it, a kludge not without drawbacks (hindering real immutability, for example).
gustafc
Also, +1 for linking to the APT article :)
gustafc
@gustafc: Of course, the correct answer is 'it depends'. IMO, the init() method or init(XXConfig) belong to `XXXConfigurable` interfaces, which are exposed to the container, but not to the actual consumer of those objects, which would receive a `YYY` interface. But these would be used to signal that they accept a `XXXConfig` object, not to force them to use it.
Robert Munteanu
A: 

Are you trying to create an object of MyInterface type through reflection?

Factory interface is a much simpler, and better, alternative.

Define a factory interface ( You can even define it as an inner interface in MyInterface ).

interface MyInterface {
  public static interface Factory {
    create( HashMap< String, String > params );
  }
}

Wherever new objects of MyInterface are created make sure it is done only through MyInterface.Factory.

On a question of generics. From the code as you provided MyInterface does not look generic at all

Alexander Pogrebnyak