Como as classes internas anônimas são usadas em Java?


Respostas:


369

Por uma "classe anônima", entendo que você quer dizer classe interna anônima .

Uma classe interna anônima pode ser útil ao criar uma instância de um objeto com certos "extras", como métodos de substituição, sem ter que realmente subclassificar uma classe.

Eu costumo usá-lo como um atalho para anexar um ouvinte de evento:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

O uso desse método torna a codificação um pouco mais rápida, pois não preciso criar uma classe extra que implemente ActionListener- posso instanciar uma classe interna anônima sem criar uma classe separada.

Eu só uso essa técnica para tarefas "rápidas e sujas", onde fazer uma aula inteira parecer desnecessária. Ter várias classes internas anônimas que fazem exatamente a mesma coisa deve ser refatorada para uma classe real, seja uma classe interna ou uma classe separada.


5
Ou você pode refatorar classes internas anônimas duplicadas em um método com a classe interna anônima (e possivelmente algum outro código duplicado).
Tom Hawtin - tackline

3
Ótima resposta, mas uma pergunta rápida. Isso significa que o Java pode viver sem classes internas anônimas e elas são uma opção extra para você escolher?
realPK 23/02

5
Muito bem explicado, ainda que difícil, sugiro que alguém leia isso para procurar e ver o que as expressões java 8 e lambda podem fazer para tornar a codificação mais rápida e legível.
Pievis

2
@ user2190639 Precisamente, não se pode pedir melhor com Lambda em Java8
bonCodigo

3
por que você disse overloading methodse não overriding methods?
Tarun

73

Classes internas anônimas são efetivamente encerramentos, portanto podem ser usadas para emular expressões lambda ou "delegados". Por exemplo, considere esta interface:

public interface F<A, B> {
   B f(A a);
}

Você pode usar isso anonimamente para criar uma função de primeira classe em Java. Digamos que você tenha o seguinte método que retorna o primeiro número maior que i na lista fornecida, ou i se nenhum número for maior:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

E então você tem outro método que retorna o primeiro número menor que i na lista fornecida, ou i se nenhum número for menor:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

Esses métodos são quase idênticos. Usando a função de primeira classe tipo F, podemos reescrevê-los em um método da seguinte maneira:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

Você pode usar uma classe anônima para usar o método firstMatch:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

Este é um exemplo realmente elaborado, mas é fácil ver que poder transmitir funções como se fossem valores é um recurso bastante útil. Veja "Sua linguagem de programação pode fazer isso", do próprio Joel.

Uma boa biblioteca para programar Java neste estilo: Java funcional.


20
infelizmente, a verbosidade de fazer programação funcional em java, IMHO, supera seus ganhos - um dos pontos marcantes da programação funcional é que ela tende a reduzir o tamanho do código e facilita a leitura e a modificação das coisas. Mas o java funcional parece não fazer isso.
Chii

27
Toda a compreensibilidade da programação funcional, com a dispersão do Java!
Adam Jaskiewicz

3
Na minha experiência, o estilo funcional em Java é pago com verbosidade antecipadamente, mas gera brevidade a longo prazo. Por exemplo, myList.map (f) é consideravelmente menos detalhado que o loop for correspondente.
Apocalisp

2
Scala , uma linguagem de estilo de programação funcional, supostamente roda bem dentro da JVM e pode ser uma opção para cenários de programação funcional.
Darrell Teague

51

Classe interna anônima é usada no seguinte cenário:

1.) Para Substituir (Subclassificação), Quando a definição de classe não é utilizável, exceto no caso atual:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) Para implementar uma interface, quando a implementação da interface é necessária apenas para o caso atual:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) Classe interna anônima definida por argumento:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
Grande resposta, parece que 3.) é o padrão usado para ouvintes de eventos
xdl

47

Às vezes, eu os uso como um hack da sintaxe para a instanciação do mapa:

Map map = new HashMap() {{
   put("key", "value");
}};

vs

Map map = new HashMap();
map.put("key", "value");

Isso economiza redundância ao fazer várias declarações put. No entanto, também encontrei problemas ao fazer isso quando a classe externa precisa ser serializada via comunicação remota.


56
Para deixar claro, o primeiro conjunto de chaves é a classe interna anônima (subclasse HashMap). O segundo conjunto de chaves é um inicializador de instância (em vez de um estático) que define os valores na sua subclasse HashMap. +1 por mencioná-lo, -1 por não soletrar para os noobs. ;-D
Spencer Kormos

4
Leia mais sobre a sintaxe de chave dupla aqui .
Martin Andersson


18

Eles são comumente usados ​​como uma forma detalhada de retorno de chamada.

Suponho que você possa dizer que são uma vantagem em comparação a não tê-las e ter que criar uma classe nomeada todas as vezes, mas conceitos semelhantes são implementados muito melhor em outros idiomas (como fechamentos ou blocos)

Aqui está um exemplo de swing

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Embora ainda seja confuso, é muito melhor do que forçar você a definir uma classe nomeada para cada ouvinte que joga fora isso (embora, dependendo da situação e reutilização, essa ainda seja a melhor abordagem)


1
Você quis dizer conciso? Se fosse detalhado, o retorno de chamada permaneceria separado, tornando-o um pouco maior e, portanto, detalhado. Se você diz que isso ainda é detalhado, qual seria uma forma concisa?
user3081519

1
@ user3081519, algo como myButton.addActionListener(e -> { /* do stuff here */})ou myButton.addActionListener(stuff)seria terser.
Samuel Edwin Ward

8

Você o usa em situações em que precisa criar uma classe para uma finalidade específica dentro de outra função, por exemplo, como ouvinte, como executável (para gerar um encadeamento), etc.

A idéia é que você os chame de dentro do código de uma função para nunca consultá-los em outro lugar, para não precisar nomeá-los. O compilador apenas os enumera.

Eles são essencialmente açúcar sintático e geralmente devem ser movidos para outro lugar à medida que crescem.

Não tenho certeza se essa é uma das vantagens do Java, mas se você as usar (e todas as usamos com frequência, infelizmente), poderá argumentar que elas são uma.


6

GuideLines para classe anônima.

  1. A classe anônima é declarada e inicializada simultaneamente.

  2. A classe anônima deve estender ou implementar para uma e apenas uma classe ou interface resp.

  3. Como a classe anonymouse não tem nome, ela pode ser usada apenas uma vez.

por exemplo:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Em relação a # 3: não é inteiramente verdade. Você pode adquirir várias instâncias de uma classe anônima com reflexão, por exemplo ref.getClass().newInstance().
icza 11/07

As regras não respondem à pergunta.
Marquês de Lorne

5

Sim, classes internas anônimas são definitivamente uma das vantagens do Java.

Com uma classe interna anônima, você tem acesso às variáveis ​​finais e membros da classe circundante, o que é útil para os ouvintes, etc.

Mas uma grande vantagem é que o código da classe interna, que é (pelo menos deve ser) fortemente acoplado à classe / método / bloco circundante, tem um contexto específico (a classe, o método e o bloco circundantes).


1
Ter acesso à classe circundante é muito importante! Eu acho que essa é a razão em muitos casos em que a classe anônima é usada, porque ela precisa / usa atributos não públicos, métodos e variáveis ​​locais da classe / método circundante que, do lado de fora (se uma classe separada fosse usada), precisariam ser aprovado ou publicado.
icza 11/07

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

Este também é um exemplo de tipo interno anônimo usando thread


3

eu uso objetos anônimos para chamar novos threads.

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

Uma classe interna está associada a uma instância da classe externa e existem dois tipos especiais: Classe local e classe Anônima . Uma classe anônima nos permite declarar e instanciar uma classe ao mesmo tempo, tornando o código conciso. Nós os usamos quando precisamos de uma classe local apenas uma vez, pois eles não têm nome.

Considere o exemplo do doc em que temos uma Personclasse:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

e temos um método para imprimir membros que correspondem aos critérios de pesquisa como:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

Onde CheckPersonestá uma interface como:

interface CheckPerson {
    boolean test(Person p);
}

Agora podemos usar a classe anônima que implementa essa interface para especificar critérios de pesquisa como:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

Aqui a interface é muito simples e a sintaxe da classe anônima parece pesada e pouco clara.

O Java 8 introduziu um termo Interface Funcional, que é uma interface com apenas um método abstrato; portanto, podemos dizer que CheckPersoné uma interface funcional. Podemos fazer uso da expressão Lambda, que nos permite passar a função como argumento do método como:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Podemos usar uma interface funcional padrão Predicateno lugar da interface CheckPerson, o que reduzirá ainda mais a quantidade de código necessária.


2

A classe interna anônima pode ser benéfica ao fornecer implementações diferentes para objetos diferentes. Mas deve ser usado com moderação, pois cria problemas para a legibilidade do programa.


1

Um dos principais usos de classes anônimas na finalização de classe, chamada de finalizador guardião . No mundo Java, os métodos finalize devem ser evitados até que você realmente precise deles. Lembre-se de que, quando você substitui o método finalize para subclasses, sempre deve invocá-lo super.finalize(), porque o método finalize da superclasse não invocará automaticamente e você pode ter problemas com vazamentos de memória.

portanto, considerando o fato mencionado acima, você pode simplesmente usar as classes anônimas como:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

Ao usar essa técnica, você e seus outros desenvolvedores se livraram de chamar super.finalize()cada subclasse do HeavyClassmétodo que precisa finalizar.


1

Você pode usar classe anônima dessa maneira

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

Parece que ninguém foi mencionado aqui, mas você também pode usar a classe anônima para armazenar argumentos de tipo genérico (que normalmente são perdidos devido ao apagamento do tipo) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

Se você instanciar essa classe de maneira anônima

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

essa holderinstância conterá uma definição não apagada do tipo passado.

Uso

Isso é muito útil para criar validadores / desserializadores. Além disso, você pode instanciar o tipo genérico com reflexão (por isso, se você quiser fazer new T()no tipo parametrizado - seja bem-vindo!) .

Desvantagens / Limitações

  1. Você deve passar o parâmetro genérico explicitamente. Não fazer isso levará à perda de parâmetro de tipo
  2. Cada instanciação custará a você classe adicional a ser gerada pelo compilador, o que leva à poluição do caminho de classe / inchaço do frasco

1

A melhor maneira de otimizar o código. Além disso, podemos usar o método de substituição de uma classe ou interface.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

Uma classe interna anônima é usada para criar um objeto que nunca será referenciado novamente. Ele não tem nome e é declarado e criado na mesma instrução. Isso é usado onde você normalmente usaria a variável de um objeto. Você substitui a variável pela newpalavra - chave, uma chamada para um construtor e a definição de classe dentro {e }.

Ao escrever um programa encadeado em Java, normalmente seria assim

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

O ThreadClassusado aqui seria definido pelo usuário. Esta classe implementará a Runnableinterface necessária para criar threads. No ThreadClasso run()método (único método Runnable) precisa ser implementado também. É claro que se livrar ThreadClassseria mais eficiente e é exatamente por isso que existem Classes Internas Anônimas.

Veja o seguinte código

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

Este código substitui a referência feita taskno exemplo mais importante. Em vez de ter uma classe separada, a Classe Interna Anônima dentro do Thread()construtor retorna um objeto sem nome que implementa a Runnableinterface e substitui o run()método. O método run()incluiria instruções internas que fazem o trabalho exigido pelo encadeamento.

Respondendo à pergunta sobre se Anonymous Inner Classes é uma das vantagens do Java, devo dizer que não tenho muita certeza, pois não estou familiarizado com muitas linguagens de programação no momento. Mas o que posso dizer é que é definitivamente um método mais rápido e fácil de codificar.

Referências: Sams ensina-se Java em 21 dias sétima edição


0

Mais uma vantagem:
como você sabe que Java não suporta herança múltipla, por isso, se você usar a classe "Thread" como classe anônima, a classe ainda terá um espaço restante para qualquer outra classe estender.

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.