Implementando várias interfaces genéricas em java


10

Preciso de uma interface que me garanta a disponibilidade de um determinado método, incluindo assinatura específica. Até agora, é o que tenho:

public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

O problema surge quando uma classe deve ser mapeada para várias outras entidades. O caso ideal seria este (não java):

public class Something implements Mappable<A>, Mappable<B> {
    public A mapTo(A someObject) {...}
    public B mapTo(B someOtherObject) {...}
}

Qual seria a melhor maneira de conseguir esse remanescente o mais "genérico" possível?

Respostas:


10

Obviamente, isso não é algo que você pode fazer devido ao apagamento de tipo . No tempo de execução, você tem dois métodos public Object mapTo(Object), que obviamente não podem coexistir.

Infelizmente, o que você está tentando fazer é simplesmente além do sistema de tipos do Java.

Supondo que seu tipo genérico seja sempre um tipo de primeira classe, e não ele próprio genérico, você poderá obter um comportamento externo semelhante ao ter o método mapTo(Object, Class), o que permitiria que você faça uma inspeção em tempo de execução da classe especificada e decida qual comportamento usar. Obviamente, isso é bastante deselegante - e exigirá a transmissão manual do valor de retorno - mas acho que é o melhor que você pode fazer. Se os seus tipos genéricos forem genéricos, seus parâmetros genéricos também serão apagados e suas Classes serão iguais, portanto esse método não funcionará.

No entanto, eu apontaria para a resposta de @ Joachim também, este pode ser um caso em que você pode dividir o comportamento em componentes separados e evitar toda a questão.


3

Como você viu, não é possível implementar a mesma interface duas vezes com parâmetros de tipo diferentes (por causa do apagamento: no tempo de execução, são as mesmas interfaces).

Além disso, essa abordagem viola o princípio da responsabilidade única: sua classe deve se concentrar em ser um Something(o que quer que isso signifique) e não deve fazer o mapeamento para Aou B além dessa tarefa.

Parece que você realmente deveria ter um Mapper<Something,A>e um Mapper<Something,B>. Dessa forma, cada classe tem uma única responsabilidade claramente definida e você não encontra o problema de implementar a mesma interface duas vezes.


Bem, a idéia é deixar a classe ser responsável por "transformar" seu conteúdo em outros objetos. Depois, há um despachante que os trata de maneira independente de classe, portanto, o requisito de genéricos. Pensarei um pouco em extrair a lógica, embora isso realmente signifique que estou dividindo a classe em duas, mas elas permanecem fortemente acopladas (o acesso ao campo deve ser concedido, a modificação de uma na maioria das vezes implica a modificação da outra, etc.)
Estani

@estani: sim, eles estão um pouco acoplados, mas têm responsabilidades distintas. Pense também sobre isso: quando você introduz uma nova classe Ce deseja Somethingser mapeável a ela, precisará modificar Something, o que é muito acoplador. Apenas adicionar um novo SoemthingToCMapperé menos invasivo.
Joachim Sauer

1
+1 para isso - em geral, você deve favorecer a composição sobre a herança, se estiver tentando alcançar o que o OP deseja (em Java). O Java 8 com métodos padrão torna isso ainda mais fácil - mas nem todos podem pular no limite :-).
precisa

0

Dado que não é permitido implementar múltiplas interfaces, você pode considerar o uso de encapsulamento. (exemplo usando java8 +)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

Por favor, verifique aqui para mais informações e mais exemplos: Como criar uma classe Java que implemente uma interface com dois tipos genéricos? .


-1
public interface IMappable<S, T> {
    T MapFrom(S source);
}

// T - target
// S - source

Se você deseja mapear User para UserDTO e mapear User para UserViewModel, precisará de duas implementações separadas. Não acumule toda essa lógica em uma única classe - não faz sentido fazer isso.

Atualize para manter Joachim feliz

public interface ITypeConverter<TSource, TDestination>
{
    TDestination Convert(TSource source);
}

Mas agora estamos no domínio Automapper ( http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters )


Não acho que IMappableseja um bom nome para algo que mapeia outras coisas. Mapper(ou IMapper, se você precisar ;-)) provavelmente está mais correto. (A propósito: não, esse não foi o meu voto negativo).
Joachim Sauer

Peguei o que estava em questão e prefixei um I para destacar o fato de que é uma interface. Estou resolvendo o problema de design conforme a pergunta, em oposição a um problema de nomeação.
CodeART 25/11/13

1
desculpe, mas na minha opinião não se pode realmente "resolver" um problema de design e ignorar a nomeação. Design significa estruturas compreensíveis. A nomeação errada é um problema para a compreensão.
Joachim Sauer

Atualização deve colocar sua mente em repouso ;-)
CodeART

1
@ Codeode Se eu entendi sua resposta corretamente, implica "MapFrom" (que deve estar em minúsculas ;-) cria o objeto. No meu caso, é apenas o preenchimento de informações sobre um objeto já criado.
Estani
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.