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

3

First I would like to say that my question is not specifically about "verifying that a generic object passed as a parameter is a subclass of an abstract class." , but I could not choose a better title. If you find a better phrase, feel free to change.

Well, my situation is this, I have an interface GerenciarClientes , which specifies operations to manage clients, an abstract class AbstractCliente that contains default parameters that every subclass of it must have, and an abstract class AbstractGerenciadorClientes that implements GerenciarClientes and has an abstract method:

ManageCustomers :

public interface GerenciarClientes{

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

AbstractClient :

public abstract class AbstractCliente{

    protected int codigo;

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

AbstractGencer :

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 , which extends AbstractGerenciadorCliente , I'm forced to implement the verificaDadosCliente(Object cliente) method 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 the class AbstractCliente I got to change the parameter of the abstract method to Class<? extends AbstractCliente> cliente , but the problem is that when I do this, I can not perform typeCast for class MeuCliente

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

    MeuCliente c = (MeuCliente) cliente; // ERRO
}
  • How can I fix this error?
  • If the ideal is to keep the original signature (passing Object cliente ) as I can verify that the object extends AbstractCliente ?
  • Everytime I override in the abstract method I have to do the typeCast inside the method? Or do you have some way to do this automatically?
  • asked by anonymous 16.08.2015 / 04:54

    1 answer

    6

    You do not need cast in the scenario you quoted. Java supports generic types from version 5, so you can say that verificaDadosCliente always expects a type that 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 signature from verificaDadosCliente to something like this:
    protected abstract <E extends AbstractCliente> boolean verificaDadosCliente(final E cliente);
    

    In this alternative, the implementation in the child class will look something 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 opinion, the best way to ensure this is to change your class AbstractGerenciador to always expect a type that inherits from AbstractCliente , because probably the expected type 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 specific 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 have to check if the type is really what you expect, even if the reference is for AbstractGerenciador , since when instantiating MeuGerenciador you will tell the compiler the expected type in the generic, something like: / p>

    AbstractGerenciador<MeuCliente> gerenciador = new MeuGerenciador();
    

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

    MeuGerenciador gerenciador = new MeuGerenciador();
    

    In both it will be guaranteed that the expected type in% with% of% with%.

    A good guide to joking with generics in java you'll find in the tutorial of Oracle's own generics .

        
    16.08.2015 / 05:27