1

First of all, pretty new in java, sorry if it is a simple question.

I have implemented an abstract class, and two classes that extends it.

public abstract class Lado{
//body
}

and the two other classes with this form for example

public class Arista extends Lado
{ //body
}

so, no problem there, now i have an interface defined like this.

public interface Grafo
{  //body
   public List<Lado> lados();
   //more body
}

has you see the interface returns a List of class Lado, but in my implementation I need to return a List of class Arista or the second class (on another implementation of the interface)

in the codebase that I HAVE to use for the interface implementation have the implementation of lados() like this

    public List<Lado> lados() {
        return lados;
    }

with lados defined in my code like

   private List<Arista> lados;

and initialized

  lados = new LinkedList<Arista>();

now of course the return gives an error

GrafoNoDirigido.java:141: error: incompatible types
    return lados;
           ^
   required: List<Lado>
   found:    List<Arista>
1 error

I cant seem to find how to fix this without modifying the base code that was given to me (and I repeat cant change it). I know I cant instantiate an abstract class object and the abstract class implementation has values and functions that the abstract class doesn't.

Also I cant use override for the same reason that i cant change my functions signatures in my implementations.

I'll appreciate your help, thanks.

If anything reads weird, please let me know.

-------------------- EDIT --------- Lado abstract class.

public abstract class Lado
{
   private String id;
   private double peso;

   public Lado(String id, double peso) {
      this.id = id;
      this.peso = peso;
   }

   public String getId() {
      return id;
   }

   public double getPeso() {
        return peso;
   }

   public abstract String toString();

 }

Arista subclass

public class Arista extends Lado
{
   private Vertice u;
   private Vertice v;

   public Arista(String id, double peso, Vertice u, Vertice v){
      super(id , peso);
      this.u = u;
      this.v = v;
   }

   public Vertice getExtremo1() {
     return u;
   }

   public Vertice getExtremo2() {
     return v;
   }


   public String toString() {
     String s = "string";
     return s;
   }

}

Then if I return a List<Lado> if y try to do getExtremo1() it doenst find the symbol on compilation (no existant on the Lado class), tried to cast the output list like a List<Arista> but it didnt worked correctly either.

  • 3
    The solution is to change the return type in the interface `Grafo` to be `List extends Lado>`. However, if you aren't allowed to change that, you're stuck. – Paul Boddington May 03 '15 at 00:21
  • Q: Which of your classes actually *implements* Grafo? – FoggyDay May 03 '15 at 00:22
  • @FoggyDay the class is implemented in a class called GrafoDirigido and another one called Digrafo, this two work at 90% I just need to make the lados() function to work correctly. pbabcdefp yes it works, unfortunately I cant change the function base signature that was given to me. Thanks. – AlejandroDiaz May 03 '15 at 01:24

5 Answers5

1

Since class Artista is also Lado class, your initialization should be

List<Lado> lados = new LinkedList<>();
:
lados.add(new Artista (..));
:
return lados;
MaxZoom
  • 7,619
  • 5
  • 28
  • 44
  • It works, but creates other problems, for example is i iterate on the list then the compiler is going to see each element as it is of class lado, and if I need to use some methos of the Arista class it fails. Thanks for your time. – AlejandroDiaz May 03 '15 at 01:15
  • When iterating ask the object what class it is by using instanceOf operator. – MaxZoom May 03 '15 at 21:11
0

Try changing your interface to:

public List lados();

public List<? extends Lado> lados();

-- OR --

You can go another route and have the list be of the parent type with child instances:

private List<Lado> lados;

@Override
public List<Lado> lados() {
    // Type of lados is List<Lado>, but we will fill it up with Arista instances...
    lados = new LinkedList<Lados>();
    lados.add(new Arista());
    // if Arista has methods that follow the with setter stereotype, ie: public Arista withId(int id) { this.setId(id); return this; } you can in-line create them:
    lados.add(new Arista()
              .withId(id)
              .withName(name)
    );
    Arista arista = new Arista();
    lados.add(arista);
    ...
    return lados;
}

Hope that helps!

anonymous
  • 657
  • 1
  • 8
  • 21
  • @LouisWasserman: I edited and added another --OR-- which will let him not need a generic type. – anonymous May 03 '15 at 00:28
  • @LouisWasserman: Thanks for the heads up, I went ahead and mucked around with some test cases until I found a working alternate to replace there. I fixed it up again, now both should be viable solutions. – anonymous May 03 '15 at 00:44
  • First one works perfectly, unfortunately already got a negative to modifiy the signatures (I don't understand this limitations). The seconds works but as I have lados() defined as a variable for my class, when I do lados = new LinkedList(); it works on the return, but if I do anything else like iterate on the list and then use a method on one of the objects then I have erros cause the compiler sees them like Lado and not like Arista. Thanks, this was the one question that worked the best – AlejandroDiaz May 03 '15 at 01:49
  • @AlejandroDiaz: I'd need to see your iteration code to see specifically what the issue there is, but you could do something like: `for (Lado lado : implClassInstance.lados()) { if (lado instanceof Arista) { Arista arista = (Arista)lado; // do more stuff here on arista. } }`, but there may be a better way to scale the code if there's more known about the behaviour model and implementation. – anonymous May 03 '15 at 02:41
  • Something in the lines of with getId() function from Arista and not on Lados, dont want to put full code cause is pretty extensive. If I use Arista elements to get an Arista it fails, if use lado ant try getId() it fails too. hope it helps to understand better the error. `ListIterator iterator = lados.listIterator(); while (iterator.hasNext()){ Lado element = iterator.next(); if(element.getId().equals("string")) return element; }` – AlejandroDiaz May 03 '15 at 03:10
  • @AlejandroDiaz: Does `Lado` have a `getId()` method. If it does and `Arista` overrides it, you should end up with the result from the child. See my answer here: http://stackoverflow.com/questions/29997018/super-issue-error-expected/29997410#29997410 or also my answer here: http://stackoverflow.com/questions/29710567/inheriting-methods-from-other-classes/29710785#29710785. Without seeing the relevant parts of each class, it's hard to help with the design or know that we offered the best solution. – anonymous May 03 '15 at 14:26
  • @anonymous in this case Lado does not have a getId(), and thats why it fails when I try to call it in the list that I return with the seconds answer. Let me try to update my question. – AlejandroDiaz May 03 '15 at 15:47
0

The problem is that Java doesn't have good support for "variance" for Enumerations (and no, it's not a simple question). Look here, under "The Workaround" for a solution: https://schneide.wordpress.com/tag/upcast/

Mattias
  • 1,110
  • 1
  • 11
  • 26
0

If you can't change either the method return type or the variable declaration, you can return a copy:

public List<Lado> lados() {
    return new LinkedList<Lado>(lados);
}

Or you can return a wrapper:

public List<Lado> lados() {
    return java.util.Collections.unmodifiableList(lados);
}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Copying works, just need to check if when i catch that return i can see check properties or functions of the Arista class. Thanks for your time. – AlejandroDiaz May 03 '15 at 01:36
-1

So these are your constraints:

  • you can't change the lados() method return type (List<Lado>).
  • you want your field to be of the List<Arista>

1. Copying the array

public List<Lado> lados() {
    return new ArrayList<>(lados);
}

2. Wrapping it as unmodifiable list*

public List<Lado> lados() {
    return Collections.unmodifiableList(lados);
}

3. Type erasure, don't do it, this is unsafe

Erasing the generics of your field before returning it. Unsafe since if someone adds an object that extends Lado but it's not Arista to that list you will have HeapPolution.

@SuppressWarnings("unchecked")
public List<Lado> lados() {
    return (List)lados;
}
Daniel Sperry
  • 4,381
  • 4
  • 31
  • 41
  • This is a terrible idea which compromises type safety and can lead to heap pollution. – Radiodef May 03 '15 at 00:39
  • Whether it compiles is not the issue. `class Foo extends Lado {} new GraphoImpl().lados().add(new Foo());` It's **not** safe and **nobody** should use it. – Radiodef May 03 '15 at 00:45
  • ok you're worried about someone modifying that list. point noted. I've modified the answer to reflect your contribution. – Daniel Sperry May 03 '15 at 00:54
  • You should just delete the raw type idea. It's bad enough without people actually recommending it. It just leads to ridiculous bugs [like this](http://stackoverflow.com/questions/29666610/classcastexception-cant-be-cast-to-java-lang-integer) and people like me have to come by and figure it out. – Radiodef May 03 '15 at 01:04
  • Nah. It's dangerous knowledge but it's knowledge. – Daniel Sperry May 03 '15 at 01:07