Function clash when implementing multiple interfaces - java

I have a variant-style object foo that is capable of behaving as a java.util.Map and a java.util.List as well as other plain-old-data types. This object is written in C++ (modelled on the composite pattern) and I'm building a JNI so that I can use it in Java.
In Java, I'm motivated to write
public class foo implements
Streamable,
java.util.Map<String, foo>,
java.util.List<foo>
Then I encounter trouble. For example, I need to implement 3 flavors of remove:
public foo remove(int index)
public boolean remove(Object key)
public foo remove(Object key)
The first two are for java.util.list, the final one for java.util.map. This, of course, is a problem since you cannot have two functions with the same name and parameters but different return types.
Is there a way round this?

An adapter would work. Have one class implement Map and Stream, and another class implement List and Stream. All operations required by these adapter's respective interfaces would draw from a common underlying foo instance.

You could use a LinkedHashMap.
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries.
Please refer to this question as it discusses the same problem you're having.

Related

Which problems can stem from overriding java.util.HashSets contains()-method?

I want to use a HashSet to store some objects:
public class StoredObject{
Type type; //Type is an enum
//other fields
Type getType(){return type;}
}
Now, I want to store only one StoredObject of the same Type, so I override contains() in a subclass of HashSet:
public MySet<E extends StoredObject> extends java.util.HashSet<E>{
#Override
public boolean contains(Object o) {
if(StoredObject.class.isAssignableFrom(o.getClass())) {//if o implements StoredObject
for(StoredObject s : this) {
if(s.getType() == ((StoredObject) o).getType()) return true;
}
}
return false
}
}
Before this I wanted to use HashSet and modify the equals() of StoredObject. However, the way above seems like a shorter and safer way, especially as in my case the stored objects all implement an interface and don't extend the same class.
Now my question: Is this implementation safe? I tried to search for things it could break, but did not find any. I read that overriding equals() can break Collections.
Also, does this subclass defeats the purpose of an HashSet, since it does not use the HashMap for contains()?
HashMap<Type,StoredObject> is the appropriate collection for this.
If you override equals(Object) then you must also override hashCode (it's also not a bad idea to make it implement Comparable and perhaps override toString). Use the #Override annotation to ensure you have the right parameter types and spelling - very easy to get wrong and confusing to debug.
What can go wrong?
There's a lot of methods in HashSet to override, so that's a lot of work.
More methods may be added to HashSet in future versions of Java - how are you going to look out for this?
contains should be an O(1) operation (assuming a good distribution of hash codes), but the OP implementation is O(n).
Set.equals on another Set will report incorrect results.
Note also that StoredObject.class.isAssignableFrom(o.getClass()) is better written as o instanceof StoredObject (assuming you've got isAssignableFrom the right way around).
Is this implementation safe?
Absolutely not. There are other methods on HashSet that wouldn't work correctly, e.g. add(), leaving the size of the set incorrect.
Besides, that implementation would totally ruin the performance of the contains method, making it run in O(n) instead of O(1).
If you need a Set with a definition of equality that differs from the objects natural definition as implemented by equals() and hashCode(), use a TreeSet and supply a custom Comparator.
class MySet<E extends StoredObject> extends java.util.TreeSet<E> {
public MySet() {
super(Comparator.comparing(StoredObject::getType));
}
}
I do agree with Tom Hawtin - tackline, that HashMap<Type, StoredObject> is a better option, because it allows you to get the StoredObject for a given Type, which is otherwise very difficult to do with a Set. It also allows you to check for existence given just a Type, without having to create a dummy StoredObject object for the check.

Why are these types not assignment compatible, and how can I define an assignment compatible class?

I implement a map based on two type arguments <K,V>, where K is the key type and V is the value type.
public class Map<K,V> implements Map<K,V>
{ .. implementation .. }
One method in the Map interfaces returns the set of map entries.
public Set<java.util.Map.Entry<K,V>> entrySet()
Since my map implementation is based on an AVL tree, another class must be implemented as Set<MapNode>
public class EntrySet<K,V> implements Set<MapNode<K,V>>
{ .. implementation ..}
In order to tell that the nodes of the map tree are indeed map entries, they are defined as follows:
public class MapNode<K,V> implements Map.Entry<K,V>
{ .. implementation .. }
So EntrySet contains the same tree as the Map itself, but treats the tree nodes as the set elements (which is why it has to be implemented separately).
The method in the map implementation, returning the set of entries, should therefore look as follows:
/* (non-Javadoc)
* #see java.util.Map#entrySet()
*/
#Override
public Set<java.util.Map.Entry<K,V>> entrySet()
{
return new EntrySet<K,V>(this.comparator,this.tree);
}
Yet, the compiler gives me the following error:
"Type mismatch: cannot convert from EntrySet<K,V> to Set<Map.Entry<K,V>>"
My questions:
Is my following assumption correct? (Or if not, what is the problem?)
A set of Map.Entry<K,V> would have to be able to contain all sorts of Map.Entry<K,V>, not just the MapNode<K,V> objects. It is therefore, logically, not the case that EntrySet<K,V> is a Set<Map.Entry<K,V>, and therefore these two set types are not considered assignment compatible.
Is there any other way to define a class EntrySet<K,V>, based on the same tree structure (I do not want to increase run time by creating or invoking another data structure) such that an object of this class can be returned wherever Set<Map.Entry<K,V> is required?
Update:
I have tried the following version:
public class EntrySet<K,V> implements Set<MapNode<K,V>>
{ .. implementation ..}
In this case the iterator creates a similar problem. There exists an iterator class that neatly traverses the whole tree, but it is defined as the iterator of a super class of MapNode<K,V>, and this iterator cannot be cast to the required Iterator<Map.Entry<K,V>>
#Override
public Iterator<Map.Entry<K,V>> iterator()
{
// this iterator type can not be cast to the desired return type
return new TreeNodeIterator<MapNode<K,V>,K>(this.tree);
}
I might copy the whole iterator code and rudely use it in a new, independent iterator class, but I'd prefer a more elegant solution.
So I am still stuck here.
Update:
In the mean time, I have created another independent iterator class that boxes the original iterator and converts the "next" element accordingly. This is not really elegant, but at least it works for the time being. Still, any more elegant solution will be very welcome!
Why are these types not assignment compatible?
The problem is that the signature of Map::entrySet() says it is supposed to return a set of any Map.Entry<K,V> objects. But your returned object only supports entries that are MapNode objects.
The (conceptual) reason that that is a problem, is a Set allows insertion as well as removal of elements, and the Set<java.util.Map.Entry<K,V>>::add(...) should be able to add any Map.Entry<K,V> to the set. But that would not be allowed by the signature of your EntrySet implementation.
How can I define an assignment compatible class?
I suggest that you try this:
public class EntrySet<K,V> implements Set<? extends Map.Entry<K,V>>
{ .. implementation ..}
or this
public class EntrySet<K,V> implements Set<Map.Entry<K,V>>
{ .. implementation ..}

Specific Collection type returned by Convenience Factory Method in Java 9

In Java 9 we have convenience factory methods to create and instantiate immutable List, Set and Map.
However, it is unclear about the specific type of the returned object.
For ex:
List list = List.of("item1", "item2", "item3");
In this case which type of list is actually returned? Is it an ArrayList or a LinkedList or some other type of List?
The API documentation just mentions this line, without explicitly mentioning that its a LinkedList:
The order of elements in the list is the same as the order of the
provided arguments, or of the elements in the provided array.
The class returned by List.of is one of the package-private static classes and therefore not part of the public API:
package java.util;
...
class ImmutableCollections {
...
// Java 9-10
static final class List0<E> extends AbstractImmutableList<E> {
...
}
static final class List1<E> extends AbstractImmutableList<E> {
...
}
static final class List2<E> extends AbstractImmutableList<E> {
...
}
static final class ListN<E> extends AbstractImmutableList<E> {
...
}
// Java 11+
static final class List12<E> extends AbstractImmutableList<E> {
...
}
static final class ListN<E> extends AbstractImmutableList<E> {
...
}
}
So this is not an ArrayList (neither a LinkedList). The only things you need to know is that it is immutable and satisfies the List interface contract.
However, it is unclear about the specific type of the returned object.
And that is all you need to know! The whole point is: these methods do return some List<Whatever> on purpose. The thing that you get back is guaranteed to fulfill the public contract denoted by the List interface. It is a list of the things given to that method, in the same order as those things were written down.
You absolutely should avoid writing any code that needs to know more about the lists returned by these methods! That is an implementation detail which should not matter to client code invoking these methods!
In that sense: your focus should be much more on the client side - for example by avoiding that raw type you are using in your example (using List without a specific generic type).
Actually the same idea is in Collectors.toList for example - you get a List back and the documentation specifically says : There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned. At the moment it is an ArrayList returned, but obviously this can change at any point in time.
I wonder if the same should be done here - to explicitly mention that the type is a List and nothing more. This let's a lot of ground for future implementations to decide what type to return that would fit best - speed, space, etc.
List.of returns a List of special type like Collections.UnmodifiableList. It is neither an ArrayList nor LinkedList. It will throw an exception when you will try to modify it.
Though the question seems to have been answered by #ZhekaKozlov and #GhostCat both in terms of what the return type would be(ImmutableCollections.List) and how it has been created package private and is not a public API. Also adding to the facts that the implementation of these factory methods guarantees to fulfill the public contract denoted by the List interface.
To further provide a gist of the implementation of the ImmutableCollections then Set, Map and List a step further in it. I would add a sample representation for a List which is quite similar for Map and Set.
The ImmutableCollections currently implements these factory methods on the interface List using:
abstract static class AbstractImmutableList<E>
which extends the AbstractList class and throws an UnsupportedOperationException for all the possible operations overriding their implementation. Meaning no more operations allowed on the collection, making them equivalent to Java Immutable Collections.
Furthermore, this is extended by a class
static final class ListN<E> extends AbstractImmutableList<E>
with a constructor to evaluate certain checks for the input and its elements being NonNull to return the object consisting of an E[] elements(array of elements of type E) and overrides certain implementations as .get(int idx), .contains(Object o) based out of these elements.
The way it goes on for Map and Set is similar, just that the validations on top of the elements or a pair of key and value are ensured there. Such that you can't create these(throws IllegalArgumentException and NullPointer exceptions respectively) :
Set<String> set2 = Set.of("a", "b", "a");
Map<String,String> map = Map.of("key1","value1","key1","value1");
Set<String> set2 = Set.of("a", null);
Map<String,String> map = Map.of("key1",null);

Usage of generic classes

I'm trying to understand generics via Java but I have some questions that is on my mind.
For example, let's think about the priority queue ADT. We can represent this ADT by an interface, which we can call MyPriorityQueue. Now, if I am not wrong, every element that we will put in a priority queue must have two components, namely the "priority value" and the "value".
We can create a type called Entry to represent the entries that will be put in a priority queue. Since the priority values and values can be of any type, if I am not wrong the Entry class should be generic.
Now, back to the priority queue ADT, if I want to implement the MyPriorityQueue interface with a class named, say MyPQ, since every priority queue's elements must be Entries, should MyPQ be generic or not?
From this point of view, it looks like should not because it will support only one type of element, which is Entry but on the other hand, it looks like it should be generic. Because, say two types of MyPQ instances, first is a to do list named toDoList and second is an airport boarding queue named boardingQueue should not be allowed to have one instance's Entries in another, meaning that one should not be allowed to attempt to put an Entry of the boardingQueue to the toDoList and vice versa.
This suggests that MyPQ should be generic. Also should MyPriorityQueue interface be generic or not? If yes, why so?
I know this has been a kind of long text but I would very much appreciate it if you could explain this to me.
Yes, you HAVE TO make MyPQ generic; you will run into compiler issues if you don't. Remember that Entry is generic, so its declaration will look something like:
public class Entry<K, V> {}
Then, your MyPQ class will use your Entry class, forcing it to be generic, like this:
public class MyPQ<K, V> implements MyPriorityQueue<K, V> {
#Override
public void add(Entry<K, V> entry){}
}
If you don't declare MyPQ as generic, then what do you replace K and V with in add(Entry<K, V> entry)? So you get a compile error.
As far as the MyPriorityQueue interface goes, you will also have to make it generic, since in there you will probably have methods with generic arguments that implementations will have to override, like public void add(Entry<K, V> entry);.
If you look at PriorityQueue, you'll see a good implementation. It's generic, but requires Comparable (or a Comparator) objects in it. Priority comes directly from the comparison and there's no need for a special Entry object.
You could recreate this in a more academic way, but it would be worthless to have an Entry class when it's not needed (unlike Map.Entry).

Why can't I call Collections.sort() on my ArrayList<T>?

For anyone who might have a question like this, you probably need "Collections.sort", not "Collection.sort", the mistake I made below.
I have defined a class defined as
public class Store implements Serializable, Comparable<Store> { ... }
I have a field in another class defined as:
ArrayList<Store> fStores = new ArrayList<Store>();
I want to sort this collection, so in a method I call:
Collection.sort(fStores);
However, I get the following compilation error:
The method sort(ArrayList<Store>) is undefined for the type Collection
ArrayList implements List, and from the documentation:
public static <T extends Comparable<? super T>> void sort(List<T> list)
So, why do I get the error? I have also tried creating my own descendant of Comparator and passing that to the sort method with no luck.
I'm guessing there's something about "< T extends Comparable< ? super T > >" I'm not understanding... ?
There are basically 2 things that you need to look at :
Collections
From the Collections
This class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers", which return a new collection backed by a specified collection, and a few other odds and ends
So basically if you have to sort or do any such kind of algorithms use this.
Next is :->
Collection
This is an interface that provides the basis of Java's collection framework. It does not include Map and Sorted Map. It represents a group of objects known as its elements and has implementations for concrete implementations. You need to think of this when you want to work with ArrayLists and Maps.
So, bottom line, you have a static algorithm to run which is present in Collections. So, use Collections.sort
You need to write Collections instead of Collection. They're related, but different. :-)
It's Collection**s**, not Collection:
http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html
vs.
http://docs.oracle.com/javase/6/docs/api/java/util/Collection.html
Did you mean Collections.sort() (collections plural)?
It's Collections.sort() with an s.
import java.util.Collections and you should be fine.
Your issue is that you are calling Collection.sort(), not Collections.sort()
These are two separate classes:
Collections
Collection
Collection is an interface while Collections is a utility class.
Also, if you are using eclipse, double check that the import statement for Collections is the right one!
In case you got here due to a similar error:
The method sort(Comparator<...>) is undefined for the type List<...>
Then you're probably using JDK lower then 1.8. Change it in Java Build Path:

Categories

Resources