Abstract class return in interface

2019-06-12 11:26发布

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.

5条回答
ゆ 、 Hurt°
2楼-- · 2019-06-12 11:48

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/

查看更多
做个烂人
3楼-- · 2019-06-12 11:48

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);
}
查看更多
不美不萌又怎样
4楼-- · 2019-06-12 11:53

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!

查看更多
疯言疯语
5楼-- · 2019-06-12 11:57

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

List<Lado> lados = new LinkedList<>();
:
lados.add(new Artista (..));
:
return lados;
查看更多
走好不送
6楼-- · 2019-06-12 12:11

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;
}
查看更多
登录 后发表回答