Jump to content

Interfaces,classes abstractas, isto é mesmo preciso?


pedrotuga
 Share

Recommended Posts

Recentemente tenho usado mais bibliotecas fora do java.* e reparei que é frequente o uso de classes abstractas e de interfaces.

Eu questiono-me se é necessário levar uma linguagem a este ponto. Não bastaria uma implementação de classes normal? Sem classes abstractas ou interfaces?

Qual seria o problema de adicionar métodos que não fazem nada a uma classe normal? Porque é que essa prática fica reservada um tipo de classes diferentes com keywords específicas?

Valerá a pena a distinção entre interfaces e classes abstractas? Qual o problema de usarum classe absracta e não definir nenhum metodo. Faz precisamente o mesmo que uma interface.

E qual seria o problema de não haver distinção ao nivel da linguagem entre os três casos ( class, abstract class e interface ) e deixar o tipo de herança a cargo do programador?

Link to comment
Share on other sites

  • Replies 40
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Uma interface permitem herança múltipla (e é precisamente por causa das características que as distinguem das classes, que a herança múltipla não é problemática), coisa que as classes (abstractas ou não, não permitem). Só este facto, justifica a utilização de interface em muitas situações.

Quanto às classes abstractas, às vezes não faz sentido nenhum implementar determinados métodos, pois pode acontecer que só as classes não abstractas é que têm a informação necessária à implementação do método.

A existência destas variantes, só aumentam a expressividade da linguagem, e aquilo que o programador pode fazer. Se o programador quiser (e conseguir) fazer tudo com classes "normais", melhor para ele, mas já tive por várias vezes de recorrer a interface (e classes abstractas também, mas não tanto).

PS: As interface são também bastante úteis quando desenvolvemos bibliotecas/frameworks. Enquanto forma de especificar comportamento, permitem dizer o que é que o programador tem que fazer para poder usar a biblioteca/framework, sem comprometer a utilização de herança por parte do programador.

Link to comment
Share on other sites

Sinceramente não sei como explicar, para mim é simplesmente natural a existencia de Interface e de Abstract Class. Uma interface permite definir um contrato e obrigar a que esse contrato seja cumprido, com classes normais não podes fazer isso. Uma interface ao definir um contracto não define qualquer tipo de implementação, és livre de implementar da forma que quiseres, com uma classe normal tinhas de aceitar o código que já exista na classe.

Em Java não existe herança multipla pelo que seria completamente impossível ter criado a plataforma com todas as funcionalidades que tem, sem usar interfaces.

Uma classe abstracta oferece um compromisso entre alguma funcionalidade definida e a necessidade de contracto.

Interfaces podem ser usadas em locais onde o código muda frequentemente, por exemplo, um dos exemplos clássicos é o de um leitor de música, que pode ler CDs, DVDs, MP3, etc, se usares interfaces podes ir incrementando o número de possibilidades, enquanto que se usares classes normais, essas opções terão de ficar hardcoded no leitor.

Muitas das coisas que se fazem em Java, muito de como a plataforma funciona, é baseado no principio de Lookup, neste caso as funcionalidades são tipo "plug-and-play", quando a plataforma executa vai procurar bibliotecas que respeitem uma interface, com classes normais este comportamento não seria possível.

Classes abstratas são optimas para frameworks, se por um lado oferecem um conjunto de funcionalidades que podes usar, por outro forçam a que um conjunto de comportamentos seja implementado por ti, novamente, com classes normais este comportamento não seria possível.

Se a distinção não existisse no sentido de 3 tipos diferentes de classes teria de existir como palavras reservadas ou outro mecanismo, seja como for, o sistema teria de existir.

No entanto é que referir que este tipo de classes existe em linguagens OO que são tipadas estáticamente (não sei se será a tradução, statically typed para ser mais correcto), noutras linguagens isso não acontece.

Com uma interface consegues tratar um conjunto de objectos distintos e obtidos de classes diferentes como se fossem todos do mesmo tipo, e isto é uma enorme vantagem.

Exitem imensos livros/papers/etc que justificam a necessidade de interfaces, é uma questão de pesquisares um pouco sobre isso 😄

Já agora, este é capaz de ser um bom ponto de partida: http://www.artima.com/designtechniques/interfaces.html

Edit:

E sim podes criar duas classes a partir de uma sem problemas.

Link to comment
Share on other sites

pedrotuga, só para ser mais fácil vens de que linguagens/tipo de programação?

Fica aqui o meu percurso de engenhocas completo.

A primeira linguagem que aprendi foi mesmo C++, na faculdade, e cheguei a desenvolver aplicações, ainda que apenas para fins didáticos, 100% orientadas a objectos, mas isto já lá vão quase 10 anitos daqui a mais.

A meio do meu curso, como tive que trabalhar muito com processamento de sinal tive que usar o matlab à bruta, ao ponto de se tornar a ferramenta programável na qual tinha mais experiencia e era mais fluente.

Depois decidi aprender mais linguagens pois já estava farto de estar limitado ao C++. Meti-me em PHP o que foi bastante facil dada a sintaxe baseada em C, PHP é ainda a linguagem em que sou mais produtivo. Em regime de hobista ainda me meti no Java, python e perl sendo que em perl não gosto muito muito de seguir abordagens orientadas a objectos dado o uso que lhe dou - para pequenos scripts de uso pontual. Mais recentemente tenho andado com a mania de aprender [common] Lisp e javascript.

Eu sou de eng electrotécnica, não de informática, a maior parte dos meus conhecimentos de programação adquiri-os enquanto auto-didata, mas tenho que admitir que as disciplicas de C++ foram uma base bem sólida, principalmente para entender o conceito de programação orientada a objectos.

Ok, tal como eu imaginava o conceito de herança multipla não é o que eu estava a pensar, seria bastante bizarro se fosse. Mas igualmente bizarra é essa expressão que afixaste (rui carlos).

Em que situação é que um programador terá necessidade de fazer uma coisa tipo

class C extends A,B{}

?

É concerteza numa situação que eu desconheço (não estou a dizer que não existe) e cuja utilizadade não se prende bem com Programação Orientada a Objectos.

Qual o construtor a usar? como se evita a colisão de nomes de membros?

Knitter, a tua resposta é precisamente o paradigma da minha dúvida existencial.

Uma linguagem deve impor restrições rígidas ao programador, ou deve dar possibilidade de usar determinado tipo de restrições caso seja necessario por uma ou outra razão?

O exemplo das frameworks não podia ser mais paradigmático, nesse caso o uso de classes abstractas serve só para "forçar". Ora, uma linguagem de programação é precisamente para expandir, criar, o que é um pouco o contrário de forçar.

Quro com isto dizer, que a framework não perde nenhuma funcionalidade se for dada mais liberdade ao programador que a vai usar, acaba por ser uma questão lhe permitir fazer mais ou menos coisa sem ser por uma limitação tecnica.

Pessoalmente não vejo interesse nisto.

Um exemplo paralelo é o do acesso ao endereço físco de um objecto/variável. Em java não existe por vários motivos, sendo que o que interessa no fim de contas é que não é preciso para nada. Não tenho problemas com isto, mas não considero válido o argumento de diz que "usa-lo seria má prática".

Link to comment
Share on other sites

Não se trata de forçar o programador no sentido de o limitar de sim de garantir que ele cumpre alguns requisitos que são necessários.

Um exemplo concreto de uso de interfaces na plataforma Java: sistema de toolkit gráfico. Java permite que, apenas colocando um jar na path do sistema ou da plataforma, alterares o toolkit que é usado. A única coisa que o teu toolkit tem de fazer, é registar-se como sendo o toolkit e implementar a interface certa.

És livre de implementar como quiseres, mas não há forma de a plataforma comunicar com o teu toolkit se não souber como comunicar, se não existir um contrato entre os dois sistemas, as interfaces permitem isso, mas só saber comunicar não é suficiente, é preciso garantir que a tua implementação cumpre os requisitos para um toolkit, e novamente, apenas as interfaces te obrigam a isso.

Mas podias simplesmente implementar todos os métodos sem o código, sendo que o teu toolkit não iria funcionar, é um ponto que a plataforma não pode controlar, mas pelo menos garantir que cumpres o contracto é necessário.

Não é uma limitação, em nada as interfaces limitam o que podes ou não programar, apenas permitem garantir um certo controlo de cumprimento do contracto que definem.

Em Java não podes faze o que o Rui disse, isso seria herança multipla e não é permitido, podes fazer algo parecido com as interfaces dado que uma classe pode implementar quantas interfaces quiseres, mas apenas pode extender de uma classe.

Assim: class A extends B, C é inválido mas interface I extends I2, I3 é código válido se I2 e I3 forem ambas interfaces.

Link to comment
Share on other sites

Ok, tal como eu imaginava o conceito de herança multipla não é o que eu estava a pensar, seria bastante bizarro se fosse. Mas igualmente bizarra é essa expressão que afixaste (rui carlos).

Em que situação é que um programador terá necessidade de fazer uma coisa tipo

class C extends A,B{}

?

É concerteza numa situação que eu desconheço (não estou a dizer que não existe) e cuja utilizadade não se prende bem com Programação Orientada a Objectos.

Qual o construtor a usar? como se evita a colisão de nomes de membros?

Curiosamente, a herança múltipla é algo que o C++ permite...

Por exemplo, em Java para usares threads precisas de estender/implementar uma classe/interface. Se o objecto já fizer parte de uma hierarquia, para estender a classe de forma a poderes lançar tarefas em novas threads, precisavas de herança múltipla.

Um exemplo, menos técnico: Tens uma classe Estudante, e tens uma classe Trabalhador, parece-me fazer sentido que uma classe TrabalhadorEstudante estenda ambas as classes anteriores.

Link to comment
Share on other sites

Curiosamente, a herança múltipla é algo que o C++ permite...

Por exemplo, em Java para usares threads precisas de estender/implementar uma classe/interface. Se o objecto já fizer parte de uma hierarquia, para estender a classe de forma a poderes lançar tarefas em novas threads, precisavas de herança múltipla.

Um exemplo, menos técnico: Tens uma classe Estudante, e tens uma classe Trabalhador, parece-me fazer sentido que uma classe TrabalhadorEstudante estenda ambas as classes anteriores.

Isto da herança múltipla é uma coisa que eu nem sabia que existia, não faço ideia que linguagens o permitem.

O exemplo das threads pode eventualmente ajudar-me a compreender a utilidade da herança multipla, mas sinceramente não o percebi. Podes explicar melhor?

Já o outro exemplo é mais genérico. Faz sentido sim, mas pare ser sincero não vejo qualquer problema em criar uma classe a partir da classe que foi herdada pelas classes Trabalhador e Estudante.

É perfeitamente possível aceder aos métodos destas duas que forem precisos sem as instancializar. Pode é por-se em causa a elegência desta prática em que se define uma classe embrulhando métodos de outras, é um pouco a questão inicial, talvez seja esse um dos motivos.

Link to comment
Share on other sites

Faz sentido sim, mas pare ser sincero não vejo qualquer problema em criar uma classe a partir da classe que foi herdada pelas classes Trabalhador e Estudante.

Não percebi o que querias dizer em "criar uma classe a partir da classe que foi herdada pelas classes Trabalhador e Estudante"...

Para conseguir essa classe que referes não precisavas de herança múltipla?

Link to comment
Share on other sites

Quis dizer: Se faz sentido herdar uma classe de duas classes existentes então é porque tambem faz sentido que essas duas classes sejam baseadas na mesma.

Porque não herdar essa tambem para o terceiro caso?

No exemplo em questão. Se a classe TrabalhadorEstudante pode ser obtida herdando Trabalhador e Estudante, então é porque estas duas últimas podiam inicialmente ter sido obtidas à custa de herança da classe Pessoa por exemplo.

Link to comment
Share on other sites

Herança multipla é algo que causa mais problemas do que os que resolve.

Esse exemplo, Trabalhador/Estudante, TrabalhadorEstudante, pode ser resolvido de várias maneiras, não tendo de se recorrer a herança multipla.

No exemplo em questão. Se a classe TrabalhadorEstudante pode ser obtida herdando Trabalhador e Estudante, então é porque estas duas últimas podiam inicialmente ter sido obtidas à custa de herança da classe Pessoa por exemplo.

Não necessariamente, o facto de um classe herdar de duas não significa que essas herdem de algo comum. Esse é um dos pontos da herança multipla, dois ou mais pais, não têm de ter algo em comum.

O exemplo das threads também não é um bom exemplo, 😄 , as threads podem ser inicadas de várias maneiras e só precisas de extender da classe Thread ou implementar a interface Runnable se pretenderes que um teu objecto seja uma thread, se quiseres apenas que um objecto possa iniciar threads não tens problemas. Se estás a tentar que um objecto seja um thread então não existirá motivo nenhum para precisares dele noutro ponto da hierarquia, o caso muito pontual que exista pode ser resolvido com interfaces e logo resolves o problema de não teres herança multipla 😄

Link to comment
Share on other sites

Peço desculpa não li todos os posts na integra mas relativamente aos interfaces eu fiquei a perceber a sua utilidade depois de ler este artigo http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/pat/poli.htm

Quanto ás classes abstractas normalmente uso para classes em que não quero construtor, em relação á herança também não é uma coisa que use muito sou um bocado mais adepto deste padrão http://www.artima.com/designtechniques/compoinh.html embora nesse artigo também levem a coisa 1 cado ao extremo.

É óbvio que já haver quem discorde mas destes padrões mas é como tudo lol

I haven’t lost my mind; it’s backed up on DVD somewhere!

Link to comment
Share on other sites

Esse segundo link não define um padrão 😄 , e não acrescenta nenhuma novidade, herança deve ser usada quando faz sentido, tal como o artigo concluí.

E se não queres um construtor na classe coloca-o como privado 😄

Não fazia ideia que existia uma coisa chamada herança múltipla. Sempre a aprender.

Herança multipla parece mais natural que o uso de interfaces mas as coisas complicam um pouco e tornam o código um pouco complicado, é mais uma daquelas coisas que C++ tem e que podes andar uma vida toda sem sequer conhecer.

Java não tem herança múltipla mas tem interfaces que podem ajudar em casos onde possa ser preciso, confesso que possa ser um conceito estranho, mas se começares a usar os problemas tornam-se bem mais simples de resolver, embora como tudo, seja preciso peso e medida.

Interfaces tem um pequeno problema que se nota muito quando as estás a usar demais, que é o facto de algumas vezes teres de repetir código em classes diferentes, é uma inconveniência que já me deu cabo dos nervos 😄

Acho que a melhor forma de se perceber isto seria passar por um problema onde as interfaces fossem a melhor solução, passei por isso quando aprendi POO e olhando para os problemas que tive de resolver, as interfaces salvaram-me a vida 😛

Uma outra utilidade das interfaces é a de marcadores, com interfaces podes "marcar" um objecto sem que tenhas de ter problemas na hierarquia, isto acontece, por exemplo, na serialização, em que usas uma interface que não tem qualquer método e que apenas permite dizer à plataforma que uma dada classe pode ser serializada.

Link to comment
Share on other sites

O exemplo das threads também não é um bom exemplo, 😄 , as threads podem ser inicadas de várias maneiras e só precisas de extender da classe Thread ou implementar a interface Runnable se pretenderes que um teu objecto seja uma thread, se quiseres apenas que um objecto possa iniciar threads não tens problemas. Se estás a tentar que um objecto seja um thread então não existirá motivo nenhum para precisares dele noutro ponto da hierarquia, o caso muito pontual que exista pode ser resolvido com interfaces e logo resolves o problema de não teres herança multipla 😄

Lá está. Ao usares uma interface, resolves o problema porque passas a ter herança múltipla. Resumindo, é um caso onde as interface são necessárias.

Se calhar não é o melhor exemplo, mas até é um caso que uso muitas vezes...

Link to comment
Share on other sites

Por acaso nem estava a pensar nessa situação, mas realmente é verdade podes usar as interfaces como se de herança multipla se tratasse, mas estava mais a pensar no uso de interfaces desta forma:

new Thread(new Runnable() {

    public void run() {
        //fazer qualquer coisa na thread.
    }).start();

Não obriguei o meu objecto onde este código está a ser uma thread, tenho uma thread  para executar o que quiser, e usei a interface Runnable 😉

Link to comment
Share on other sites

Por acaso nem estava a pensar nessa situação, mas realmente é verdade podes usar as interfaces como se de herança multipla se tratasse, mas estava mais a pensar no uso de interfaces desta forma:

new Thread(new Runnable() {

    public void run() {
        //fazer qualquer coisa na thread.
    }).start();

Não obriguei o meu objecto onde este código está a ser uma thread, tenho uma thread  para executar o que quiser, e usei a interface Runnable 😉

Nem sempre uso essa solução, pois frequentemente preciso de chamar um método da superclasse no método run, e dessa forma a superclasse passa a ser a Object (pois estás dentro da classe Thread). Pelo menos não conheço nenhuma forma de aceder ao método da superclasse assim.

Link to comment
Share on other sites

Estamos a desviar-nos do tópico mas só para dar a solução, não a única mas uma delas.

As classes anónimas podem aceder a todos os atributos das classes onde foram declaradas e a todos os parâmetros finais do método onde foram declaradas, assim, o seguinte código é válido:

public class UsarThreadSemSerThread {
    
    private UsarThreadSemSerThread me = this;
    
    private void metodoPrivado1() {
        System.out.println("Metodo privado 1");
    }
    
    private void metodoPrivado2() {
        System.out.println("Metodo privado 2");
    }
    
    private void criarThread(final int parametro1) {
        final String variavel = "variavel de metodo com parametro: ";
        
        new Thread(new Runnable() {

            public void run() {
                me.metodoPrivado1();
                metodoPrivado2();
                System.out.println(variavel + parametro1 );
            }
        }).start();
    }
    
    public static void main(String[] args) {
        new UsarThreadSemSerThread().criarThread(50);
    }

}

O uso de variáveis/parâmetros finais tem a ver com a forma como o compilador constrói a classe interna, neste caso o compilador precisa de uma garantia de que o valor não muda por isso o modificador final.

O uso do atributo me é apenas para verem um atributo da instancia a passar para a classe anónima. De referir que não é necessário dado que as classes anónimas possuem uma referencia para a classe onde estão declaradas mas podem deparar-se com uma situação em que seja preciso.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.