Pass arguments that extends from same abstract class to function - java

I finished some exercise but I wanted to optimize it a little bit.
I have two classes name "U1" and "U2", both extend the same abstract class called "Rocket" and I have two functions called "loadU1" and "loadU2" that returns ArrayList (U1 or U2) and i wanted to optimize it to one function without success. I tried the generic type, but I cant figure out how should I know which type to return or cast to. Does it possible?
Note: I didn't share some code that I have tried because I got no idea where should I start even (JAVA Newbie).
This is the strcture of the classes:
public class U1 extends Rocket {}
public class U2 extend Rocket {}
abstract class Rocket implements SpaceShip {}
These are the current functions:
public ArrayList<U1> loadU1(ArrayList<Item> items) {
ArrayList<U1> u1Rockets = new ArrayList<>();
return u1Rockets;
}
public ArrayList<U2> loadU2(ArrayList<Item> items) {
ArrayList<U2> u2Rockets = new ArrayList<>();
return u2Rockets;
}

Maybe you are looking for something like this:
public <T extends Rocket> List<T> load(List<Item> items) {
List<T> rockets = new ArrayList<>();
return rockets;
}

Related

Java generics challenge

Description
Firstly, all code to reproduce is shown below as well as with a UML diagram of the implementation. I understand that the naming I have gone for can be confusing, but I'm not able to come up with anything better for this minimal working example of my issue at hand.
The essence of the problem is in class AChildOne and is related to these three lines of code:
List<C<B>> cBList = new ArrayList<>();
List<C<BChild>> cBChildList = aChildTwo.get();
cBList.addAll(cBChildList); // <-- COMPILATION ERROR
If I change the first line in the above code snippet to:
List<C<? extends B>> cBList = new ArrayList<>();
the specific compilation error goes away, but it breaks other parts of the class.
If I remove the container class C from this implementation, things seem to work. However, in the actual implementation it does a bit more than what is shown in this minimal example so it is not so easy to remove it.
I was thinking that I would like to do something like this:
public class AChildOne implements A<? extends B>
but this is not allowed.
I suppose the problem is that I'm not able to successfully combine the generics for the return value of AChildOne::get() with the return value of AChildTwo::get().
I suspect that what I'm trying to do here is one of those 'gotchas' with generics, that you are not allowed to do.
Side note
Using Number, Integer and Double instead of my own classes, I'm essentially trying to do something like this:
public static void main(String[] args) {
List<C<Integer>> integerList = new ArrayList<>();
integerList.add(new C<>(1));
List<C<Double>> doubleList = new ArrayList<>();
doubleList.add(new C<>(3.14));
List<C<? extends Number>> numberList = new ArrayList<>();
numberList.addAll(integerList);
numberList.addAll(doubleList);
numberList.forEach(n -> System.out.println(n.value));
}
which does indeed work (C here is the same C as implemented below). However, when wrapping these things in separate classes, and using inheritance, it does not seem like I'm able to propagate the generics properly. If ONLY I could do this:
public class AChildOne implements A<? extends B>
I think it would work.
UML diagram
Code
import java.util.List;
public interface A<T> {
List<C<T>> get();
}
import java.util.ArrayList;
import java.util.List;
public class AChildOne implements A<B> {
private final AChildTwo aChildTwo = new AChildTwo();
public List<C<B>> get() {
List<C<B>> cBList = new ArrayList<>();
List<C<BChild>> cBChildList = aChildTwo.get();
cBList.addAll(cBChildList); // <-- COMPILATION ERROR
return cBList;
}
}
import java.util.ArrayList;
import java.util.List;
public class AChildTwo implements A<BChild> {
List<C<BChild>> list = new ArrayList<>();
public List<C<BChild>> get() {
return list;
}
}
public class B {
}
public class BChild extends B {
}
public class C<T> {
T value;
public C(T value) {
this.value = value;
}
T get() {
return value;
}
}
AChildOne cannot implement A<B>, because it cannot supply a List<C<B>>. It supplies a List<C<BChild>> (the return type of aChildTwo.get()). A List<C<BChild>> is not a subtype of List<C<B>>. If it were, it would break type safety:
List<C<BChild>> a = ...;
List<C<B>> b = a; // suppose you could do this
C<B> c = b.get(0);
c.value = new AnotherChildOfB();
BChild bchild = a.get(0).get(); // this would get an AnotherChildOfB instance, but is declared to return a BChild.
As you have correctly identified, AChildOne.get could return a List<C<? extends B>>. You would need to change the interface method's signature to make this work, rather than changing the inheritance clause.
Alternatively, AChildOne could implement A<BChild>. I actually recommend you choose this solution, because returning a wildcard is likely a code smell.

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.

Java : Generics and TypeCasting

I am developing a security program I am facing a big problem about generics.
Consider the following code :
abstract class Dictionary{
List<Entity> list;
public Dictionary(List<Entity> list){
this.list = list; // Error
}
}
Entity is a user defined interface and It has two implementing classes:
interface Entity{
...
}
class BinaryEntity implements Entity{
}
class ListEntity implements Entity{
}
The problem raises in use of inheritance.I have a concrete class called binary dictionary which extends abstract class dictionary.it contains a list of binary entity the problem is when i want to pass the list to super class constructor there is a error I know the following statement is wrong :
ArrayList<BinaryEntity> a;
List<Entity> b = a;\\erro
I have tried upper and lower bounding but still in trouble help please.
Sorry for the long question. Thanks in advance
You'll need to make Dictionary generic as well, e.g.:
abstract class Dictionary<E extends Entity> {
List<E> list;
public Dictionary(List<E> list){
this.list = list;
}
}
class BinaryDictionary extends Dictionary<BinaryEntity> {
public BinaryDictionary(List<BinaryEntity> list) {
super(list);
}
// ... custom methods
}

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

Java collections covariance problem

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)
... ;
}

Categories

Resources