Ensure that generic object passed as parameter is subclass of an abstract class

Asked

Viewed 3,315 times

3

First I would like to say that my doubt is not specifically about "verify that a generic object passed as parameter is subclass of an abstract class.", but I couldn’t pick a better title. If you find a better phrase, feel free to change.

Well, my situation is this, I have an interface GerenciarClientes,that specifies operations to manage customers, an abstract class AbstractCliente containing parameters default that every subclass of her should have, and an abstract class AbstractGerenciadorClientes implementing GerenciarClientes and has an abstract method:

Managerial:

public interface GerenciarClientes{

    void adicionar(Object cliente);
    void remover(Object cliente);
    void editar(Object cliente);
}

Abstract client:

public abstract class AbstractCliente{

    protected int codigo;

    public AbstractClient(int codigo){
        this.codigo = codigo;
    }
}

Abstractmanager:

public class AbstractGerenciador implements GerenciarClientes{

    /* Construtor */

    @Override
    void adicionar(Object cliente);

    @Override
    void remover(Object cliente);

    @Override
    void editar(Object cliente);

    protected abstract boolean verificaDadosCliente(Object cliente);
}

My problem is this: When I create the class MeuGerenciador, extending AbstractGerenciadorCliente i am obliged to implement the method verificaDadosCliente(Object cliente) and pass an object as a parameter. In this case, I would like the object to be a subclass of AbstractCliente ( suppose the name of the subclass is MeuCliente). In class AbstractCliente I changed the abstract method parameter to Class<? extends AbstractCliente> cliente, but the problem is that when I do this, I can’t perform Typecast for class MeuCliente

protected abstract boolean verificaDadosCliente(Class< ? extends AbstractCliente>  cliente){

    MeuCliente c = (MeuCliente) cliente; // ERRO
}
  1. How can I fix this error?
  2. If the ideal is to keep the original signature (passing Object cliente) how can I verify that the object extends AbstractCliente?
  3. Every time I give override in the abstract method I have to do Typecast within the method? Or have some way to do it automatically?
  • 1

    Declare the method as verificaDadosCliente(AbstractCliente cliente) does not solve? Then you call him passing the object of type MeuCliente as a parameter, which will work because it is also a AbstractCliente.

  • @Piovezan The problem is that MeuCliente can own methods, therefore it would not be possible to access them using AbstractCliente

  • @but if you NEED the methods in MeuCliente, no use trying to do generic like that. You would have to use verificaDadosCliente(MeuCliente cliente), since it is not any instance of AbstractCliente that will work.

1 answer

6


You don’t have to cast in the scenario you quoted. Java supports generic types since version 5, so you can say that verificaDadosCliente always waits for a guy who inherits from AbstractCliente, and the concrete type will be informed in the concrete implementation of AbstractGerenciador.

So, apparently, you can do what you want in two ways:

  • in the subclasses of AbstractGerenciador what is the expected concrete type, so you can use something like this:
public abstract class AbstractGerenciador<E extends AbstractCliente> implements GerenciarClientes {

    // outros métodos

    protected abstract boolean verificaDadosCliente(final E cliente);

}

public class MeuGerenciador extends AbstractGerenciador<MeuCliente> {

    @Override
    protected boolean verificaDadosCliente(final MeuCliente cliente) {
        return true;
    }

}
  • change the signature of verificaDadosCliente for something like this:
protected abstract <E extends AbstractCliente> boolean verificaDadosCliente(final E cliente);

In this alternative, the implementation in the child class will look like this:

@Override
protected <T extends AbstractCliente> boolean verificaDadosCliente(final T cliente) {
    final Class<?> providedType = cliente.getClass();
    final Class<MeuCliente> expectedType = MeuCliente.class;
    if (!expectedType.isAssignableFrom(providedType)) {
        final String message = "Tipo esperado não corresponde ao informado. Esperado '%s', encontrado '%s'.";
        throw new IllegalArgumentException(String.format(message, expectedType.getName(), providedType.getName()));
    }

    final MeuCliente obj = expectedType.cast(cliente);

    final boolean result = false;

    // faz o que precisa na verificação

    return result;
}

In my view, the best way for you to ensure this is by changing your class AbstractGerenciador to always wait for a guy who inherits from AbstractCliente, because probably the kind expected in adicionar, remover and editar will also be the same.

A possible final version of your objects would look like this:

  • interface GerenciarClientes:
public interface GerenciarClientes<E extends AbstractCliente> {

    void adicionar(final E cliente);

    void remover(final E cliente);

    void editar(final E cliente);

}
  • abstract class AbstractGerenciador:
public abstract class AbstractGerenciador<E extends AbstractCliente> implements GerenciarClientes<E> {

    @Override
    public void adicionar(final E cliente) { }

    @Override
    public void remover(final E cliente) { }

    @Override
    public void editar(final E cliente) { }

    protected abstract boolean verificaDadosCliente(final E cliente);

}
  • a concrete customer(MeuCliente):
public class MeuCliente extends AbstractCliente {

    public MeuCliente() {
        super(1);
    }

}
  • a concrete manager(MeuGerenciador):
public class MeuGerenciador extends AbstractGerenciador<MeuCliente> {

    @Override
    protected boolean verificaDadosCliente(final MeuCliente cliente) {
        return true;
    }

}

With this you do not need to check if the type is really what you expect, even if the reference is for AbstractGerenciador, since to instantiate MeuGerenciador you will tell the compiler the type expected in generic, something like this:

AbstractGerenciador<MeuCliente> gerenciador = new MeuGerenciador();

If the reference is for the concrete type you will not inform the concrete type of AbstractCliente, just stay like this:

MeuGerenciador gerenciador = new MeuGerenciador();

In both will be guaranteed that the type expected in verificaDadosCliente MeuCliente.

A good guide to playing with generic java tutorial of Oracle’s own generics.

  • Perfect! But still has a small "problem". These classes are part of a small framework, so I think the API would not be very user-friendly as I would have to specify the expected object in all classes and methods :/

  • @regmoraes as well as so specify the expected object in all classes and methods? You treat as generic in its abstract type, only the concrete type will know the concrete "entity", for example MeuCliente. When using the managed ones, if the reference is for the abstract type, otherwise it does not need. I will update with this to see if it solves any more points

  • when I said specify the expected object in all classes and methods , meant that the developer will have to write <MeuCliente> in several locations ( in the class MeuGerenciador, AbstractGerenciador). But I do not think that this is a big "problem", unfortunately it is the price that is paid for providing flexibility

  • @regmoraes is not really a problem anyway, in case you’ve used frameworks market, like spring, will see that they use it a lot and we need to inform the concrete type. Even the java API, see for example the collections we need to report on, either when we use it or when we create a new implementation for, for example, List<E>, which is a raw type =)

Browser other questions tagged

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