Java collections covariance problem - java

Lets say we have a program which contains such classes:
public interface AbstractItem {
}
public SharpItem implements AbstractItem {
}
public BluntItem implements AbstractItem {
}
public interface AbstractToolbox {
//well the problem starts here...
public List<AbstractItem> getItems();
}
public ExpensiveToolbox implements AbstractToolbox {
private List<SharpItem> items = new ArrayList()<SharpItems>;
public List<SharpItem> getItems() { return this.items; }
}
public CheapTooblox implements AbstractToolbox {
private List<BluntItem> items = new ArrayList()<BluntItem>;
public List<BluntItem> getItems() { return this.items; }
}
Easy, right? Well lets say we now want to make a method like this (in some random class):
public void doImportantStuff(AbstractToolbox toolbox) {
//important stuff!
//this obviously won't work
List<AbstractToolbox> items = toolbox.getItems();
//do some stuffwith all items
}
Now the problem is that in Java collections with generics aren't covariant (hope that's the term I'm looking for) and I can't assign an ArrayList<ExpensiveToolbox> to a List<AbstractToolbox>. The only solution I can see here is to duplicate the code and do a version for each type, but that would obviously suck (what if we had more classes implementing AbstractToolbox with different lists?). Oh obviously the second solution would be to drop the generics and make a normal List, but is it a good practice?
Are there any design pattern/practices to tackle such problems?
#Edit: ok so I might not be precise enough. I want all the classes which extend AbstractToolbox to have a List of certain classes which extend AbstractItem and then I want a method that will take an AbstractToolbox as a parameter and do something on the items in its list (using the classes that would be defined in AbstractItem so all the items of every possible list would actually have them).

You're probably going to need to take a look at using wildcard types for generics. Here's a quick link: What is PECS (Producer Extends Consumer Super)?
Quick answer: change the type to List<? extends AbstractItem>
Why can't you just assign this?
Imagine the code here...
List<AbstractItem> foo = new ArrayList<SharpItem>();
foo.add(new BluntItem());
The static typing says this should work... but you can't do that! It would violate the ArrayList's type. That's why this is disallowed. If you change it to
List<? extends AbstractItem> foo = new ArrayList<SharpItem>();
you can then do the assignment, but never add anything to the list. You can still retrieve elements from the list, however, as AbstractItems.
Is just using List (bare type) a good solution?
No, definitely not :-p

Here are a couple of extra ideas. Leave everything the same, but use this:
interface AbstractToolbox {
public List<? extends AbstractItem> getItems();
}
This basically says that the abstract class' items are an unknown type, but subclasses can make it concrete. This would require you to call getItems() on a reference of type ExpensiveToolbox or CheapToolbox to be able to retrieve a list that allows you to add items, etc.
ExpensiveToolbox toolbox = new ExpensiveToolbox();
AbstractToolbox absTB = toolbox;
List<? extends AbstractItem> items1 = absTB.getItems(); //fine
List<SharpItem> items2 = absTB.getItems(); //compile error
List<SharpItem> items3= toolbox.getItems(); //fine
Alternatively, you could just type AbstractToolbox:
public interface AbstractToolbox<T extends AbstractItem> {
public List<T> getItems();
}
public ExpensiveToolbox implements AbstractToolbox<SharpItem> {
public List<SharpItem> getItems() { //...
}

public interface AbstractItem
{
}
public class SharpItem implements AbstractItem
{
}
public class BluntItem implements AbstractItem
{
}
public interface AbstractToolbox<T extends AbstractItem>
{
public List<T> getItems();
}
public class ExpensiveToolbox implements AbstractToolbox<SharpItem>
{
private List<SharpItem> items = new ArrayList<SharpItem>();
public List<SharpItem> getItems() { return this.items; }
}
public class CheapToolbox implements AbstractToolbox<BluntItem>
{
private List<BluntItem> items = new ArrayList<BluntItem>();
public List<BluntItem> getItems() { return this.items; }
}
public void doImportantStuff(AbstractToolbox<?> toolbox)
{
List<? extends AbstractItem> items = toolbox.getItems();
for(AbstractItem item : items)
... ;
}

Related

Java generics cast and usage

I have kind of problem with generics inheritance. Below is the dependency tree:
public class Attributes {
}
public abstract class Feature<T extends Attributes> {
private T attributes;
public T getAttributes() {
return attributes;
}
public void setAttributes(T attributes) {
this.attributes = attributes;
}
}
public abstract class AbstractFeatureRepository<T extends Feature<? extends Attributes>> {
public abstract String getType();
public abstract boolean create(T feature);
}
And I have implementations of these feature repositories, like this:
public class NodeAttributes extends Attributes {
private String startPoint;
public String getStartPoint() {
return startPoint;
}
public void setStartPoint(String startPoint) {
this.startPoint = startPoint;
}
}
public class Node extends Feature<NodeAttributes> {
}
public class NodeRepository extends AbstractFeatureRepository<Node> {
public String getType() {
return "Node";
}
public boolean create(Node node) {
return true;
}
}
public class LinkAttributes extends Attributes {
private String uri;
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
public class Link extends Feature<LinkAttributes> {
}
public class LinkRepository extends AbstractFeatureRepository<Link> {
public String getType() {
return "Link";
}
public boolean create(Link link) {
return true;
}
}
I'm injecting these repositories with Spring to Controller via constructor (but in this example I'm manually creating in constructor for the sake of simplicity):
public class Controller {
private final Map<String, AbstractFeatureRepository<? extends Feature>> featureRepositories;
public Controller() {
this.featureRepositories = Arrays.asList(new NodeRepository(), new LinkRepository()).stream()
.collect(Collectors.toMap(AbstractFeatureRepository::getType, Function.identity()));
}
public Node createNode() {
Node newNode = new Node();
newNode.getAttributes().setStartPoint("Berlin");
createFeature("Node", newNode);
return newNode;
}
public Link createLink() {
Link newLink = new Link();
newLink.getAttributes().setUri("/home/local");
createFeature("Link", newLink);
return newLink;
}
private void createFeature(String type, Feature<? extends Attributes> feature) {
featureRepositories.get(type).create(feature);
}
}
All is good untill I want to call "create" method in generic "createFeature" where I'm getting compilation error that
The method create(capture#5-of ? extends Feature) in the type AbstractFeatureRepository<capture#5-of ? extends Feature> is not applicable for the arguments (Feature<capture#6-of ? extends Attributes>)
What I'm doing wrong? Is this because potentially I can pass some other kind of "Feature" than particular "Repository" can work with? How then should I change my map Repositories in Controller so compiler doesn't complain? I though I should operate exact classes as a key for map but have no idea how to make all of this working. Any suggestions?
Thank you.
Update 1. I changed Map to
private final Map<Class<?>, AbstractFeatureRepository<? extends Feature>> featureRepositories;
Changed AbstractFeatureRepository so it now looks this way:
public abstract class AbstractFeatureRepository<T extends Feature> {
public abstract Class<T> getClazz();
public abstract boolean create(T feature);
}
And changed the methods in controller:
public Link createLink() {
Link newLink = new Link();
createFeature(Link.class, newLink);
return newLink;
}
private <T extends Feature> void createFeature(Class<T> class1, T feature) {
AbstractFeatureRepository<? extends Feature> abstractFeatureRepository = featureRepositories.get(feature.getClass());
abstractFeatureRepository.create(abstractFeatureRepository.getClazz().cast(feature));
}
It still doesn't allow me to do that.
This code:
featureRepositories.get(type)
returns an object whose type is the V in Map<K, V>, as per the docs of java.util.Map. In your code, that means that expression is of type AbstractFeatureRepository<? extends Feature<? extends Attributes>>.
Let's simplify matters a tad, and assume we have List<? extends Number> instead.
This is valid java code:
List<? extends Number> list = new ArrayList<Integer>();
that's sort of the point of ? extends, really. This does not compile:
List<Number> list = new ArrayList<Integer>();
Now, imagine you called, on your List<? extends Number>:
List<? extends Number> list = new ArrayList<Integer>();
Number n = Double.valueOf(5.0);
list.add(n);
uhoh. There is a non-integer in my list of integers. Whoops.
That's why you can't call add() here, at all. You cannot call add on a List<? extends whatever>, at all. Any method that takes as argument a T where your type is Foo<? extends T> cannot be invoked*.
Let's go back to your code now:
You have a AbstractFeatureRepository<? extends Feature<? extends Attributes>> - therefore any method that AbstractFeatureRepository has that takes a T cannot be invoked from this. at all. And create is such a method.
The solution is a bit tricky. You can use a type-safe container, if you somehow have a reference to a class representing T (careful; things can be a T that cannot be a Class: List<Integer> is a T, but only List.class exists, you can't write List<Integer>.class! - You can use that:
public <T extends Attribute> void createFeature(Class<T> typeDesired, Feature<T> feature) {
featureRepositories.get(type).create(typeDesired.cast(feature));
}
is one way.
In general your method signature is problematic: There is just no guarantee that your stringly-typed String type implies that the kind of feature desired Feature<? extends Attribute> is handled by the repository matching that type.
A second option is to have each AbstractFeatureRepository responsible to deal with type mismatches. In that case, you can update the interface to read create(Object feature) throws TypeMismatchException instead. Or, have it return a type (public Class<T> getType()) and you can go back to the cast construct.
*) Well, you can invoke it, if you pass literally null, as null is all data types. But that clearly isn't what you intend to do here, thus, not relevant.
If you've only got 2 things in a Map (or N things, where N is a small number, you mention 4 in a comment), and you use fixed keys to indicate a particular type, using a Map is making it harder than necessary.
Maps are necessarily homogeneously typed, that is, all the keys have a common supertype, and all the values have a common supertype. The issue that you're facing is that you want a key's type to relate to the value's type: this can be done with a type-safe heterogeneous container (essentially: a map which you construct such that you can impose the constraints on the relationships between the types). But even this is overly-complex for the problem described.
Use two (N) fields instead:
public class Controller {
private final NodeRepository nodeRepository = new NodeRepository();
private final LinkRepository linkRepository = new LinkRepository();
This is still sort-of a map: the key is the field, the value is the field value.
But the advantage of this is that you've retained the concrete types of the repositories, so you can pass these to the method:
private <A extends Attributes> void createFeature(AbstractFeatureRepository<A> repo, Feature<A> feature) {
repo.create(feature);
}
e.g.
public Node createNode() {
Node newNode = new Node();
newNode.getAttributes().setStartPoint("Berlin");
// Or, easier, nodeRepository.create(newNode);
createFeature(nodeRepository, newNode);
return newNode;
}
public Link createLink() {
Link newLink = new Link();
newLink.getAttributes().setUri("/home/local");
// Or, easier, linkRepository.create(newNode);
createFeature(linkRepository, newLink);
return newLink;
}
To arrive at a working solution that is as close to your original code as I could get it, I made three or four relatively minor refactors.
The most significant change though was in Controller.createFeature()…
private <T extends Feature<?>> void createFeature(Class<T> class1, T feature) {
AbstractFeatureRepository<T> abstractFeatureRepository = (AbstractFeatureRepository<T>)featureRepositories.get(feature.getClass());
abstractFeatureRepository.create(feature);
}
The cast there is the simplest, most type safe solution in my opinion. The reason I'm convinced the cast is type safe is because of the compilation error you'd get if the cast weren't there:
Controller.java:31: error: incompatible types: AbstractFeatureRepository<CAP#1> cannot be converted to AbstractFeatureRepository<T>
AbstractFeatureRepository<T> abstractFeatureRepository = featureRepositories.get(feature.getClass());
where T is a type-variable:
T extends Feature<?> declared in method <T>createFeature(Class<T>,T)
where CAP#1 is a fresh type-variable:
CAP#1 extends Feature<?> from capture of ? extends Feature<?>
1 error
If you read the bottom part of the error message carefully, you'll see that the only difference between T extends Feature<?> and CAP#1 extends Feature<?> is the names of the two type variables. They both have the same upper bounds (extends Feature<?>). That tells me it's reasonable to infer that a cast would be type safe.
So, I annotated that method with SuppressWarnings("unchecked").
To confirm that the solution is usable, I fleshed out Node and Link classes with toString(). Calling Controller.createNode() and Controller.createLink() in the solution demo gets you…
Node: [NodeAttributes - Start Point: 'Berlin']
Link: [LinkAttributes - URI: 'urn::foo::bar']
I have to admit that what problem you're trying to solve isn't crystal clear to me. So I've made some assumptions based on only my general Java knowledge. Please let me know if the solution meets your requirements?
Here is the approach I used:
public class Controller {
private final Map<Class<?>, AbstractFeatureRepository<? extends Feature>> featureRepositories;
public Controller3(List<AbstractFeatureRepository<? extends Feature>> featureRepositories) {
this.featureRepositories = featureRepositories.stream()
.collect(Collectors.toMap(AbstractFeatureRepository::getClazz, Function.identity()));
}
public Node createNode() {
Node newNode = new Node();
createFeature(Node.class, newNode);
return newNode;
}
public Link createLink() {
Link newLink = new Link();
createFeature(Link.class, newLink);
return newLink;
}
private <T extends Feature> void createFeature(Class<T> clazz, T feature) {
AbstractFeatureRepository<T> repository = getRepository(clazz);
repository.create(feature);
}
#SuppressWarnings("unchecked")
private <T extends Feature, V extends AbstractFeatureRepository<T>> V getRepository(Class<T> clazz) {
return (V) featureRepositories.get(clazz);
}
public static void main(String[] args) {
Controller3 controller = new Controller3();
controller.createLink();
}
}
It doesn't satisfy completely no-cast approach(especially no #SuppressWarnings) but it is the least evil for me, since cast is done only in one method in controller, all the rest methods work no cast and no #SuppressWarnings.
Try
private static <T extends AbstractFeatureRepository> void createFeature(Class<T> clazz, Feature<? extends Attributes> feature) {
((T) featureRepositories.get(clazz)).create(feature);
}
You should modify the featureRepositories accordingly
private static final Map<Class<?>, AbstractFeatureRepository<? extends Feature>> featureRepositories
But I don't recommend using generics like this.

Using self-referential generic types in Java

Consider the following Java method:
<T extends List<T>> List<T> getMyList() {
return Collections.emptyList();
}
I can assign its output to a variable with a raw type, like so:
List x = getMyList();
List<List> y = getMyList();
But, I can't think of any way to assign its output to a fully parameterized type. In particular, I can't think of a non-raw, concrete type T that would satisfy List<T> z = getMyList();
Can we create such a T ?
If not, why not?
For context, I created this question while trying to understand how Enums are implemented in Java.
Here's an example of a concrete type that both works and starts to hint at a possible use-case (registration of some sort). The type consists acts like both an instance of some type, and as a container for all instances of that type.
public class WeirdEnum extends AbstractList<WeirdEnum> {
private static List<WeirdEnum> underlyingList = new ArrayList<>();
#Override
public WeirdEnum get(int index) { return underlyingList.get(index); }
#Override
public int size() { return underlyingList.size(); }
static <T extends List<T>> List<T> getAList() {
return Collections.emptyList();
}
public WeirdEnum() {
underlyingList.add(this); // Sufficient for our example but not a good idea due to concurrency concerns.
}
static List<WeirdEnum> foo = WeirdEnum.getAList();
}
Not sure if I fully understand your question, but here's an example:
class Example<T> implements List<Example<T>> {
...
}
...
List<Example<String>> list = getMyList();
Every enum in Java extends from the base-enum-class Enum<T extends Enum<T>>, where T is the actual type of the implementing enum.
When writing SomeClass<T extends SomeClass<T>> you can enforce that the type-parameter is always the implementing class itself.
Let's say you have this interface:
public interface MyInterface<T extends MyInterface<T>> {
T getSelf();
}
And this implementing class:
public class MyClass implements MyInterface<MyClass> {
public MyClass getSelf() {
return this;
}
}
In MyClass it is not possible to use any other type-parameter than MyClass itself.

How to return List<Impl> for List<interface>

The following code is pretty useless and not tested. It should only explain the problem.
I want to hide the implementing classes from the application, but define the hierarchy.
Given following interfaces
public interface RowType {
Integer getSumFromHere();
}
public interface TableType {
List<RowType> getRows();
}
Implemented by
public class RowImpl implements RowType {
private Integer value = 0;
private RowImpl nextRow;
public RowImpl someFunctionNotInInterface() {
return nextRow;
}
#Override
public Integer getSumFromHere() {
return nextRow == null ? value : value + nextRow.getSumFromHere();
}
}
public class TableImpl implements TableType {
List<RowImpl> implList = new ArrayList<>();
public void doSomethingOnImpl (){
for(RowImpl row : implList) {
row.someFunctionNotInInterface();
}
}
#Override
public List<RowType> getRows() {
return implList;
}
}
The implementation of getRows() leads to an error "cannot convert from List<RowImpl> to List<RowType>"
In fact, it`s guaranteed that every entry in the implList can be accessed through the interface RowType, so it could work.
I tried <? extends RowType> but that's not compatible to the TableType interface.
Of course I can simple solve the problem, by copying the list return new ArrayList<>(implList); but that's not the same as having a reference to the list held by the class.
Is there a solution for this, or is the design completely wrong ?
edit: Added function in TableImpl that clarifies why the list is build on RowImpl and not on RowType.
implList is a List<RowImpl> and should contain only RowImpl instances.
The List<RowType> you return has an add(RowType) method, for example, that could be used to add RowType instances that are not RowImpl.
For this reason, List<RowType> is not a supertype of List<RowImpl> and you will have to cast implList if you want to return it.
At the same time, you should make sure that it is not modified by the caller, so that it really can only contain RowImpl instances.
The Collections.unmodifiableList() method does both of those jobs:
#Override
public List<RowType> getRows() {
return Collections.unmodifiableList(implList);
}
The easiest solution from where you are could be just type-casting your list elements:
public class TableImpl implements TableType {
List<RowImpl> implList = new ArrayList<>();
#Override
public List<RowType> getRows() {
return implList.stream().map(e -> (RowType) e).collect(Collectors.toList());
}
}
A better solution, though, can involve redesigning your API, to make TableType aware of the type of RowType it holds. This is only good if you allow code that calls getRows() to know about the implementation class RowImpl (otherwise the previous solution is suitable).
public interface TableType<T extends RowType> {
List<T> getRows();
}
With this, your implementation class can use RowImpl by implementing this version of the interface in this way:
public class TableImpl implements TableType<RowImpl> {
List<RowImpl> implList = new ArrayList<>();
#Override
public List<RowImpl> getRows() {
return implList;
}
}
You should change the generic type to
List<RowType> implList = new ArrayList<>();
rather than:
List<RowImpl> implList = new ArrayList<>();
When inserting an element into the List, you can guarantee that it has the required type. For example:
implList.add(new RowImpl());

How do I properly extend this abstract class?

Hi I'm inexperience with Java. I understand the concepts of inheritance but I think the syntax is eluding me. I'm seeking some help to get me started in extending this abstract class:
I need to create a concrete object from it.
What this class should do is take in a type during initialization and store a list of objects of that type. Sort them and then return a list of n top objects when showTopN is called.
I have not started implementing the logic yet.
abstract class Foo<T extends Comparable<T>> {
int n;
Foo(int n){ // constructor; sets object property n
this.n = n;
}
abstract void push(T object); //object method to store a new object in the list
abstract List<T> showTopN(); // object method to return top n entries in the list, sorted.
}
I've tried to extend this into a concrete object this way:
class ConcreteFoo extends Foo {
private List<Foo> fooList;
public void push(Foo object) {
}
#Override
public List<Foo> showTopN() {
return fooList;
}
#Override
public int compareTo(ConcreteFoo other) {
return 0;
}
}
But the compiler is complaining that I have not overridden the push method.
What is wrong?
There are two things going on here. One is the "abstractness" of Foo, but the other is the Generics. You have neglected the generics aspect.
If you know the type of object that your Concrete foo cares about, you can just use that:
class ConcreteFoo extends Foo<SomeKnownClass> {
private List<SomeKnownClass> list = new ArrayList<SomeKnownClass>();
void push(SomeKnownClass skc) {}
List<SomeKnownClass> showTopN() { return list; }
}
Now, if you don't know the type of it, you can still use generics:
class ConcreteFoo<T extends Comparable<T>> extends Foo<T> {
private List<T> list = new ArrayList<T>();
void push(T skc) {}
List<T> showTopN() { return list; }
}
Note that neither Foo nor ConcreteFoo implement Comparable, so you don't need the compareTo method.
The push method specifies that it will accept a T object, which is Foo's generic type, which you haven't declared. If you want Foo to be a List of itself, which I'm not certain that you do, you'd have to declare it as
class ConcreteFoo extends Foo<Foo> {
But I think you need to re-examine your basic principles.
You're conflating a container with the objects that it contains. The class structure that you want is something like:
class Foo implements Comparable<Foo> { ... }
abstract class GenericContainer<T> {
abstract void push(T object); //object method to store a new object in the list
abstract List<T> showTopN(); // object method to return top k entries in the list, sorted.
}
class FooContainer extends GenericContainer<Foo> {
private List<Foo> fooList;
...
}
Your showTopN method can then be something like:
public List<Foo> showTopN() {
return Collections.sort(fooList).subList(0, n);
}

Setting Java generics of iterators

I've noticed something funny Java does (or at least Netbeans) when I use classes implementing ArrayList and changing the generics type of the elements. I basically created an abstract class that extends ArrayList and some subclasses that are supposed to work with String objects (so something like ArrayList<String>). One of the things I did to try to achieve that was this:
public abstract class A extends ArrayList {
...
}
#Override
public abstract class B extends A {
public Iterator<String> iterator() {
return super.iterator();
}
}
Another one was this:
public abstract class A extends ArrayList {
...
}
public abstract class B<String> extends A {
#Override
public Iterator<String> iterator() {
return super.iterator();
}
}
The first one overrides successfully the iterator() method assigning a String value to it. The other one somehow cancels out the type casting. The funny thing is that none of them works when it comes to for loops. This receives type Object instead of String.
for (String s : B) {
...
}
Do you have any idea why this happens and how can I fix it without implementing my own iterator?
Not sure what you are trying to do but if I understand correctly you want a class that extends ArrayList and has a Generic type of String... Perhaps you are looking for this:
public abstract class A<T> extends ArrayList<T> {
...
}
public abstract class B extends A<String> {
...
}
Then in your code, this:
B myList = ...;
for ( String s : myList ) {
...
}
Will work just fine. Though I think you could come up with a much better solution. Do you have more specifics about your problem?
Use composition instead of Inheritance
public class A implements Iterable<String>{
List<String> myList = new ArrayList<String>();
//do operations on myList
public Iterator<String> iterator() {
return myList.iterator();
}
}
If you extend generic class you should care about generics. I mean that your declaration should look like
public abstract class A extends ArrayList<String> {
...
}
if you want to use strings or
public abstract class <T> A extends ArrayList<T> {
...
}
if you want your class to be generic.
In both cases you do not have to override iterator() method: you can invoke its from super class and it will return you "good" iterator. Your declaration is equivalent to
public abstract class A extends ArrayList<Object> {
...
}
This is the reason for "strange" behavior.
BTW may I ask you why are you extending ArrayList? It really sounds strange.
OMG this is terrible:
public abstract class B<String> extends A {
#Override
public Iterator<String> iterator() {
return super.iterator();
}
}
String is not the class String, rather, you are declaring a new type variable called String (like T) that shadows the class String

Categories

Resources