I have a class that contains a collection. I want to be able to specify what implementation is used at runtime. What is the best object oriented way to accomplish this?
public class Klazz {
private class Data {
...
}
private Collection<Data> collection;
public Klazz(?) {
}
}
How can I make it so the constructor specifies what type of collection is used to implement Klazz.
In Java, List, Set and Queue Interfaces are derived from java.util.Collection Interface. Hence all the objects of any implementations of List, Set and Queue can be referenced by a Collection reference.
For a detailed explanation please refer to this very good guide at geeksforgeeks
tl;dr you can do something like:
public class Klazz {
private class Data {
...
}
private Collection<Data> collection;
public Klazz(Collection<Data> collection) {
this.collection = collection;
}
public static void main(String[] args) {
Klazz k1 = new Klazz(new ArrayList<>());
Klazz k2 = new Klazz(new HashSet<>());
}
}
The classic way to allow a Java client to choose an implementation class is to use a Factory Object supplied by the client to instantiate the object.
Something like this:
public interface CollectionFactory {
<T> Collection<T> create();
}
public class Klazz {
private Collection<Data> collection;
public Klazz(CollectionFactory <Data> factory) {
collection = factory.create();
}
}
(I haven't checked the above with the compiler, so there could be typos, etc)
Passing in a collection object is another solution. There is potentially an abstraction leakage, but the same is true with a factory object if the factory is "tricky".
It is also possible to pass a Class object and use reflection to instantiate the collection.
#Tim Biegeleisen has a point though. The Collection API does not constrain the properties of the collection a great deal, and use-cases which will work equally for collections that are lists, sets or "bags" are .... unusual.
Related
I would like to know if there is a more efficient way of storing values (like fields) for an instance of an interface (if implementing it is not guaranteed) than a static hashmap in an other non-visible class.
Example:
public interface myInterface {
public default Object getMyVariable() {
return Storage.data.get(this);
}
}
final class Storage {
static HashMap<myInterface, Object> data = new HashMap<myInterface, Object>();
}
Firs of all this is bad bracrice - you abstraction knows about implementations. The point of interface is to introduce abstraction - and get rig of rigit design.
You can define interface like that:
public interface MyInterface {
default Object getMyVariable() {
return getDefaultObject();
}
Object getDefaultObject()
}
As you can see i added required method getDefaultObject() - that all implementations have to implement. However this will not work if you already have implementation classes - and you have no control over them.
I'm working on a project where I've got multiple classes to hold data loaded from an xml file. The problem I would like to solve is that almost all of the classes have these methods:
addSingle[objectName]
addMultiple[objectName]
However, there may be more types of objects in a class that need to be added this way. For example:
class Airspace {
private List airports;
private List waypoints;
...
public void addSingleAirport(Airport a) {...}
public void addMultipleAirports(Airport[] a {...}
public void addSingleWaypoint(Waypoint w) {...}
public void addMultipleWaypoints(Waypoint w) {...}
}
I was thinking of putting those addSingle and addMultiple methods into an interface and then decide, when implementing the methods, according to the objects type to which list it should be added, but wouldn't that be stupid?
Is there any more efficient way to do this? I want to avoid writing these methods into every class that needs them with the specific objects because they all do exactly the same.
I don't think you approach is stupid but it is true this type checking can affect performance.
Nevertheless, expanding your approach, I would keep a map of lists indexed by Class:
class Airspace {
private Map<Class, List<Object>> lists = new HashMap();
public void addSingle(Object o)
{
List<Object> list = lists.get(o.getClass());
if(list == null)
{
list = new ArrayList();
lists.put(o.getClass(), list);
}
list.add(o);
}
...
...
public void addAirports(Airport... a ){...}
should be able to handle all cases - or did I miss something?
A very general
public void addThings( Object... o )
can be written, but do you want to lose readability and type checking? Runtime errors if there's no matching Foo-list in an object? Shudder.
You can use an Abstract class with generics then extends it from you class. Something like this:
public abstract class AbstractSomeclass<E>{
public void addObject(E... obj, List<E>){
//yourcode here
}
}
Then in your class:
public class MyClass extends AbstractSomeclass<Airport>{
public List<Airport> list;
//.....
public void someMethod(){
super.addObject( airportObject, list );
}
}
Maybe there is some minor errors (on this code), but hold on with the idea.
Let's say I'm building an immutable Yahtzee scorecard class:
public final class Scorecard {
private Map<Category, Integer> scorecard = new HashMap<Category, Integer>();
public Scorecard() {
// Instantiates a new empty scorecard
}
private Scorecard(Map<Category, Integer> scorecard) {
this.scorecard = scorecard;
}
public Scorecard withScore(Category category, int[] roll) {
newScorecard = new HashMap<Category, Integer>(scorecard); // Pretend that this is a deep-copy
newScorecard.put(category, calculateScoreFromRoll(roll));
return new Scorecard(newScorecard);
}
public int getScore(Category category) {
return scorecard.get(category);
}
}
Basically I don't want to expose the internals of the class. If I didn't have a private constructor then I would need to use a public constructor with a Map argument just like the private one (and I could essentialy lose the withScore() method too) in order to allow scoring. But is this a valid way of doing factory methods?
A very common, and good pattern is to have all private constructors and public static factory methods:
public class MyClass {
private MyClass() {}
public static MyClass fromA(A foo) {
MyClass o = new MyClass();
o.field = bar; // etc
return o;
}
public static MyClass fromB(B foo) {
MyClass o = new MyClass();
o.field = bar; // etc
return o;
}
}
Note: This allows different factory methods with the same parameter types, which constructors do not allow.
Factory methods are intended to allow you to get an object without specifying the exact type.
For example, from Effective Java, 2nd edition:
The class java.util.EnumSet (Item 32), introduced in release 1.5, has no public constructors, only static factories. They return one of two implementations, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array.
The existence of these two implementation classes is invisible to clients. If RegularEnumSet ceased to offer performance advantages for small enum types, it could be eliminated from a future release with no ill effects. Similarly, a future release could add a third or fourth implementation of EnumSet if it proved benefi- cial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet.
Using constructors instead of static methods like you suggested breaks the factory method pattern, because by using the constructor directly you are specifying an implementation.
In your case, if you want to use a factory method you would make the default constructor private so clients could not directly instantiate a ScoreCard. At this point, you're free to use whatever specific implementation of ScoreCard in the factory method. For example, if you make a second ScoreCard class that is backed with a TreeMap, you can switch which implementation of ScoreCard that the client gets just by changing the static factory.
Take, for example, immutability. How could I modify an object to indicate that it has been made immutable already and need not be wrapped again?
Let us assume we do not want to use reflection to scan for setters as that would be inefficient and insufficient.
Example:
// Deliberately chosing lowercase because it is a system attribute.
interface immutable {
// Nothing in here I can think of.
}
// immute - have I invented a new word?
// What can I do with the return type to indicate immutability?
public static <T> List<T> immute(List<T> list) {
// If it's not an immutable
if (!(list instanceof immutable)) {
// Make it so - how can I stamp it so?
return Collections.<T>unmodifiableList(list);
}
// It is immutable already.
return list;
}
Further playing with the idea produced this foul solution - it is horrible and almost any other trick would be better but I felt I should post. Please please find a better solution:
public class Test {
// Deliberately chosing lowercase because it is a system attribute.
interface immutable {
// Nothing in here I can think of.
}
// immute - have I invented a new word?
// What can I do with the return type to indicate immutability?
public static <T> List<T> immute(List<T> list) {
// If it's not an immutable
if (!(list instanceof immutable)) {
// Make it so - how can I stamp it so?
return Hacker.hack(Collections.<T>unmodifiableList(list),
List.class,
immutable.class);
}
// It is immutable already - code DOES get here.
return list;
}
public void test() {
System.out.println("Hello");
List<String> test = new ArrayList<>();
test.add("Test");
test("Test", test);
List<String> immutableTest = immute(test);
test("Immutable Test", immutableTest);
List<String> immutableImmutableTest = immute(immutableTest);
test("Immutable Immutable Test", immutableImmutableTest);
}
private void test(String name, Object o) {
System.out.println(name + ":" + o.getClass().getSimpleName() + "=" + o);
}
public static void main(String args[]) {
new Test().test();
}
}
class Hacker {
// Hack an object to seem to implement a new interface.
// New interface should be instanceof testable.
// Suggest the additional type is an empty interface.
public static <T> T hack(final Object hack,
final Class<T> baseType,
final Class additionalType) {
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{baseType, additionalType},
new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Always invoke the method in the hacked object.
return method.invoke(hack, args);
}
});
}
}
If the check will be done on the same location, you could use a set or map, where you put all your wrapped objects, and check them later on in almost constant time. To avoid memory leaks, you could wrap them using weak references .
If the introduction of AOP is a (rather heavyweight) option, you could solve your problem using inter type declarations via AspectJ. This way, you could just add a private member with the reference to the corresponding wrapped instance to the Collection interface, if I remember correctly something like this:
aspect Unmodifieable {
private Collection java.util.Collection.unmofifieableWrapper = null;
public Collection java.util.Collection.getUnmodifieable() {
if (unmofifieableWrapper == null) {
unmofifieableWrapper = somehowRetrieveUnmodifieableCollection(this);
}
return unmofifieableWrapper;
}
}
You can do this with naming conventions in your classes.
interface MyObject;
class MyMutableObject implements MyObject;
class MyImmutableObject implements MyObject;
In my current project, I do something similar. I have an interface that needs to have a setter, but one of the implementing classes is immutable. When you call its setter it throws an Exception (it's setter should never be called, but it's there just to be safe).
The "information" you're looking for is more for the programmer than the compiler, so you don't need a language implemented "stamp".
The Collections.unmodifiable* methods return subtypes of UnmodifiableCollection so you could check UnmodifiableCollection.class.isAssignableFrom(list) then test the concrete types.
Without using instrumentation, I think you're stuck checking types.
How would I modify this so the resulting Collection (newNodes) is the same type as the incoming one (nodes)?
public void setNodes(Collection<NodeInfo> nodes) {
Collection<NodeInfo> newNodes = new TreeSet<NodeInfo>();
for (NodeInfo ni: nodes) {
newNodes.add(ni.clone());
}
}
I suspect it's something like...
public void setNodes(<T extends Collection<NodeInfo>> nodes) {
Collection<NodeInfo> newNodes = new T<NodeInfo>()
Is this possible?
Unfortunately, it's not possible as you've written it in Java. If you need this effect, you've got a few choices:
If you're trying to optimize for a particular kind of collection, you can use an instanceof check to detect it. (For instance the Guava libraries often do this to detect immutable collections and handle them specially.)
If you really just need one collection to populate, you can ask the caller to provide you one.
public <C extends Collection<NodeInfo>> void setNodes(C nodes, C newNodes) {
for (NodeInfo ni : nodes) {
newNodes.add(ni);
}
}
If you need the ability to make an arbitrary number of these collections on demand, then you can define a factory interface and make the caller provide an instance of it:
interface Factory<C extends Collection<NodeInfo>> {
C newCollection();
}
public <C extends Collection<NodeInfo>> void setNodes(C nodes, Factory<C> factory) {
C newNodes = factory.newCollection();
for (NodeInfo ni : nodes) {
newNodes.add(ni);
}
}
Close, but no cigar. If I understand what you want to do, your method should look like:
public <T extends NodeInfo> void setNodes(Collection<T> nodes) {
Collection<T> newNodes = new TreeSet<T>();
for(T t : nodes) {
newNodes.add(t);
}
}
Unfortunately, you cannot do new T in Java: Since generics are implemented in Java via type erasure, the type information that is provided by a type parameter is only statically usable information, i.e. no longer available at runtime. Hence Java does not permit generic creation of objects (cf. Angelika Lange's Generics FAQ).
Alternatively, you could use:
a type token, i.e. use a Class<T> object as parameter to make the type available at runtime
use the signature void setNodes(Collection<NodeInfo> nodes, Collection<NodeInfo> newNodes) if you are able to create the suitable Collection elsewhere
use a standard Collection implementation if one is suitable, e.g. ArrayList<NodeInfo>
deep clone nodes, e.g. using The Cloning Library:
Cloner cloner=new Cloner();
#SuppressWarnings("unchecked") Collection<NodeInfo> newNodes = cloner.deepClone(nodes);
Note that many of the Collection implementations in the JDK implement Cloneable themselves. One "best effort" approach might be like this:
public Collection<NodeInfo> setNodes(Collection<NodeInfo> nodes) throws CloneNotSupportedException {
Collection<NodeInfo) newNodes;
if (nodes instanceof Cloneable)
newNodes = (Collection<NodeInfo>) newNodes.clone();
else
// Fallback in case we have a non-cloneable collection
newNodes = new TreeSet<NodeInfo>();
newNodes.clear();
for (NodeInfo ni: nodes) {
newNodes.add(ni.clone());
}
return newNodes;
}
This returns the same kind of collection for many inputs, but will fall back to returning a TreeSet as a default if it can't do any better.