Doubt with inheritance in Java method

Asked

Viewed 375 times

6

I have the interface below

public interface BaseRelatorioDTO extends Serializable {

    public BaseFiltroDTO getFiltro();

    public List<? extends BaseRespostasDTO> getRespostas();

}

And I’d like to create the method

public void setRespostas(final List<? extends BaseRespostasDTO> respostas);

But when creating this method, all classes that implement BaseRelatorioDTO and already have this method begin to give the error

Name Clash: The method setRespostas(List<? extends RespostaHorariosDTO>) of type RelatorioHorariosDTO has the same setRespostas(List<? extends BaseRespostasDTO>) of type BaseRelatorioDTO but does not override it.

The following is an example of one of the classes:

public class RelatorioHorariosDTO implements BaseRelatorioDTO {

    private static final long serialVersionUID = -3828618335258371680L;

    private FiltroHorariosDTO filtro = new FiltroHorariosDTO();
    private List<RespostaHorariosDTO> respostas = new ArrayList<RespostaHorariosDTO>();

    @Override
    public FiltroHorariosDTO getFiltro() {
        return this.filtro;
    }

    @Override
    public List<RespostaHorariosDTO> getRespostas() {
        return this.respostas;
    }

    /**
     * @param respostasParam the respostas to set
     */
    public void setRespostas(final List<RespostaHorariosDTO> respostasParam) {
        this.respostas = respostasParam;
    }
}

If you look at my method setRespostas wait as parameter a list of RespostaHorariosDTO, this class is written as below:

public class RespostaHorariosDTO implements BaseRespostasDTO {

    private static final long serialVersionUID = 5505724855293262084L;

    // Atributos e métodos acessores
}

What I’m doing wrong that the method cannot be declared on the interface so I force all classes that implement BaseRelatoriosDTO implement the method setRespostas?

  • could show the implementation of setRespostas(List) of type RelatorioCaixaVisitaEmpresaDTO ?

  • @Math Uma is RelatorioHorarios the other RespostaHorarios.

  • truth, sorry the mistake, could meet my first request then?

  • @Math I copied one of the errors that happened for all classes that implement BaseRelatorioDTO. This same mistake happens to RelatorioHorariosDTO, I’ll change the name of the class in the error I reported, OK?

  • @Math I’ve changed. Your request has been answered as you expected?

  • I believe that yes, is that I am without time now, I will be able to see it later, thanks

  • @Math I appreciate your help!

Show 2 more comments

2 answers

3

The problem is that Java discards generic types after compilation (such as Erasure that the error is mentioning), so that two functions with the same signature varying only the generic types are interpreted as the same function. Example:

interface A {
    public void foo(int x);           // OK
    public void bar(List<Integer> x); // Após compilação vira:
                                      // public void bar(List x)
}

class B implements A {
    public void foo(long x) { ... }       // Não tem problema: é overload, não override

    public void bar(List<Long> x) { ... } // Após compilação vira:
                                          // public void bar(List x)
                                          // Ops, é igual o da interface, então é override
                                          // Mas o tipo genérico é diferente... E agora?
}

Unfortunately, I believe that you cannot have this method the way you would like. By the principle of Liskov’s replacement, if RelatorioHorariosDTO implements BaseRelatorioDTO then you should be able to use an object of that class wherever an instance that performs that interface would be expected. Therefore, suppose that:

class MeuDTO implements BaseRespostasDTO { ... }
List<MeuDTO> lista = new ArrayList<MeuDTO>();

BaseRelatorioDTO x = new RelatorioHorariosDTO();
x.setRespostas(lista);

The last line is valid: BaseRelatorioDTO.setRespostas accepts any List of which the generic type is a subtype of BaseRespostasDTO, and lista meets this requirement. However, the class RelatorioHorariosDTO only accepts lists with RespostaHorariosDTO, then passing this list would be a mistake. The compiler cannot solve this dilemma, so it prohibits constructs of this type.

The alternative is any implementation of BaseRelatorioDTO have a simple method:

public void setRespostas(List<BaseRespostasDTO> respostasParam)

and make Casts when necessary... (example in the ideone) There may be other options, but I don’t have enough experience with Java generics to indicate a satisfactory alternative.


Updating: why the compiler accepts a different return value on getRespostas, but not a different parameter in setRespostas? Because in Java the return types are covariants, but the parameters are invariants:

interface C {
    public A foo();
    public void bar(A param);
}

class D implements C {
    public B foo() { ... }            // OK, override em C.foo
    public void bar(B param) { ... }  // O parâmetro é diferente: é overload, não override
                                      // (não importa se é subclasse, ainda assim é overload)
                                      // Faltou void bar(A param) - erro
}

That is, if Java supported input types covariants (almost no language gives - except Eiffel) so you could write your classes as they are. If Java supported input types contravariants (many languages support), you could use a more general type (e.g..: List<Object>), but not a more specific one. But as in Java the input is invariant, to be considered override the method parameters must be identical to the superclass/interface.

  • But why, then, does it work for the getRespostas() as you can see in the code I presented?

  • @Philippegioseffi Great question, maybe I’m completely wrong. The same logic I used pro setRespostas should also be worth to getRespostas. In that case, you’ve tried to mark subclasses with @Override and see if the compiler accepts? I will do some additional tests, when I come back here with the results.

  • 1

    By placing this annotation it generates another error saying that I should override the method in the hierarchy and I am not doing it, so I cannot use this annotation. If you repair use this annotation in get's of the implementing classes BaseRelatorioDTO.

  • @Philippegioseffi It cost to drop the plug, but my original answer is correct. See the edition. In Java output types are covariants, but types of input are invariants. That is, what the compiler accepts in the return value will not necessarily accept in the function parameter. getRespostas is okay, setRespostas nay... :(

2

This reply this great.

If you want a solution for the implementation:

public class RelatorioHorariosDTO implements BaseRelatorioDTO<RespostaHorariosDTO> {

    private static final long serialVersionUID = -3828618335258371680L;

    private FiltroHorariosDTO filtro = new FiltroHorariosDTO();
    private List<RespostaHorariosDTO> respostas = new ArrayList<RespostaHorariosDTO>();

    @Override
    public FiltroHorariosDTO getFiltro() {
        return this.filtro;
    }

    @Override
    public List<RespostaHorariosDTO> getRespostas() {
        return this.respostas;
    }

    @Override
    public void setRespostas(List<RespostaHorariosDTO> respostasParam) {
        // TODO Auto-generated method stub

    }
}

And for the interface:

public interface BaseRelatorioDTO<T extends BaseRespostasDTO> extends Serializable {

    public BaseFiltroDTO getFiltro();
    public List<T> getRespostas();
    public void setRespostas(final List<T> respostasParam);

}

Edited

Added generic methods setRespos.

  • And how would the setRespostas?

  • Unfortunately it gave the same error that I reported at the beginning of the question.

  • 1

    There’s no mistake here. You have correctly added the generic types Basereporteriodto<Respostahorariosdto> and interface Basereportariodto<T extends Baserespostasdto>???

Browser other questions tagged

You are not signed in. Login or sign up in order to post.