首页 文章

如何在Java中使用匿名(内部)类?

提问于
浏览
278

Java中匿名类的用途是什么?我们可以说匿名类的使用是Java的优势之一吗?

18 回答

  • 70

    我使用匿名对象来调用新的线程..

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

    匿名内部类是有效的闭包,因此它们可以用于模拟lambda表达式或“委托” . 例如,使用此界面:

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

    您可以匿名使用它在Java中创建first-class function . 假设您有以下方法返回给定列表中大于i的第一个数字,或者如果没有数字更大则返回i:

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

    然后你有另一种方法返回给定列表中小于i的第一个数字,或者如果没有数字更小则返回i:

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

    这些方法几乎相同 . 使用第一类函数类型F,我们可以将它们重写为一个方法,如下所示:

    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;
    }
    

    您可以使用匿名类来使用firstMatch方法:

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

    这是一个非常人为的例子,但很容易看出能够传递函数就像它们是值一样是一个非常有用的功能 . 见乔尔本人的"Can Your Programming Language Do This" .

    这个风格的Java编程库很好:Functional Java.

  • 6

    我有时使用它们作为Map实例化的语法攻击:

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

    VS

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

    在执行大量put语句时,它可以节省一些冗余 . 但是,当外部类需要通过远程处理序列化时,我也遇到了问题 .

  • 17

    优化代码的最佳方法 . 另外,我们可以使用类或接口的重写方法 .

    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();
        }
    
    }
    
  • 2

    匿名内部类可以是有益的,同时为不同的对象提供不同的实现 . 但是应该非常谨慎地使用它,因为它会给程序的可读性带来问题 .

  • 44

    是的,匿名内部类绝对是Java的优势之一 .

    使用匿名内部类,您可以访问周围类的final和member变量,并且在侦听器等中派上用场 .

    但是一个主要的优点是内部类代码(至少应该)与周围的类/方法/块紧密耦合,具有特定的上下文(周围的类,方法和块) .

  • 4

    还有一个优点:
    如您所知Java不支持多重继承,因此如果您使用"Thread" kinda类作为匿名类,则该类仍有一个空间可供任何其他类扩展 .

  • 7

    class-finalization中匿名类的主要用法之一,称为终结器监护 . 在Java世界中,应该避免使用finalize方法,直到您真正需要它们为止 . 你必须记住,当你覆盖子类的finalize方法时,你也应该总是调用 super.finalize() ,因为超类的finalize方法不会自动调用,你可能会遇到内存泄漏问题 .

    所以考虑到上面提到的事实,你可以使用匿名类,如:

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

    使用这种技术,您可以放心自己和其他开发人员在 HeavyClass 的每个子类上调用 super.finalize() ,这需要finalize方法 .

  • 343

    您可以在需要在另一个函数内为特定目的创建类的情况下使用它,例如,作为侦听器,作为runnable(以生成线程)等 .

    这个想法是你从一个函数的代码中调用它们,所以你永远不会在别处引用它们,所以你不需要命名它们 . 编译器只是枚举它们 .

    它们基本上是语法糖,一般应该随着它们变大而移到别处 .

    我不确定它是否是Java的优点之一,但如果你确实使用它们(不幸的是我们经常使用它们),那么你可以说它们是一体的 .

  • 45

    您可以通过这种方式使用匿名类

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

    匿名内部类用于以下场景:

    1.)对于Overriding(子类),当除了当前情况之外的类定义不可用时:

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

    2.)对于实现接口,仅在当前情况下需要实现接口时:

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

    3.)Argument Defined匿名内部类:

    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");
           } 
         });
       } 
     }
    
  • 4
    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();
    

    这也是使用线程的匿名内部类型的示例之一

  • 1

    通过"anonymous class",我认为你的意思是anonymous inner class .

    当创建具有某些“额外”的对象的实例(例如重写方法)时,匿名内部类可以变得有用,而不必实际上对类进行子类化 .

    我倾向于使用它作为附加事件监听器的快捷方式:

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

    使用这种方法可以使编码更快一点,因为我不需要创建一个实现 ActionListener 的额外类 - 我可以实例化一个匿名内部类而不实际创建一个单独的类 .

    我只使用这种技术进行“快速而肮脏”的任务,使整个 class 感觉不必要 . 有多个完全相同的匿名内部类应该重构为一个实际的类,不管它是一个内部类或单独的课程 .

  • 0

    它们通常用作冗长的回调形式 .

    我想你可以说它们比不拥有它们更有优势,并且每次都必须创建一个命名类,但类似的概念在其他语言中实现得更好(如闭包或块)

    这是一个摆动的例子

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

    虽然它仍然很复杂,但它比强迫你为这样的每个扔掉的监听器定义一个命名类要好得多(尽管取决于情况和重用,这可能仍然是更好的方法)

  • 0

    似乎没有人在这里提到,但你也可以使用匿名类来保存泛型类型参数(通常由于类型擦除而丢失):

    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;
        }
    }
    

    如果您将以匿名方式实例化此类

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

    那么这样的 holder 实例将包含传递类型的非擦除定义 .

    用法

    这对于构建验证器/解串器非常方便 . 你也可以用反射实例化泛型类型(所以如果你想在参数化类型中做 new T() - 欢迎你!) .

    缺点/局限

    • 您应该显式传递泛型参数 . 如果不这样做将导致类型参数丢失

    • 每个实例化将花费您额外的类由编译器生成,这会导致类路径污染/ jar膨胀

  • 1

    inner class与外部类的实例相关联,有两种特殊类型:本地类和Anonymous class . 匿名类使我们能够同时声明和实例化一个类,从而使代码简洁 . 当我们只需要一个本地类时,我们使用它们,因为它们没有名称 .

    考虑doc中的示例,其中我们有一个 Person 类:

    public class Person {
    
        public enum Sex {
            MALE, FEMALE
        }
    
        String name;
        LocalDate birthday;
        Sex gender;
        String emailAddress;
    
        public int getAge() {
            // ...
        }
    
        public void printPerson() {
            // ...
        }
    }
    

    我们有一种方法来打印符合搜索条件的成员:

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

    其中 CheckPerson 是一个界面,如:

    interface CheckPerson {
        boolean test(Person p);
    }
    

    现在我们可以使用实现此接口的匿名类来指定搜索条件:

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

    这里的界面非常简单,匿名类的语法看起来很笨拙且不清楚 .

    Java 8 引入了一个术语Functional Interface,这是一个只有一个抽象方法的接口,因此我们可以说 CheckPerson 是一个功能接口 . 我们可以使用Lambda Expression,它允许我们将函数作为方法参数传递:

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

    我们可以使用标准功能接口 Predicate 代替接口 CheckPerson ,这将进一步减少所需的代码量 .

  • 0

    匿名类的GuideLines .

    • 匿名类同时声明和初始化 .

    • 匿名类必须扩展或实现一个且只能实现一个类或接口 .

    • 由于匿名类没有名称,因此只能使用一次 .

    例如:

    button.addActionListener(new ActionListener(){
    
                public void actionPerformed(ActionEvent arg0) {
            // TODO Auto-generated method stub
    
        }
    });
    
  • 5

    Anonymous Inner Class 用于创建永远不会再次引用的对象 . 它没有名称,在同一语句中声明和创建 . 这通常用于您通常使用对象变量的位置 . 使用 new 关键字替换变量,调用构造函数以及 {} 中的类定义 .

    在Java中编写Threaded程序时,通常看起来像这样

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

    这里使用的 ThreadClass 将是用户定义的 . 该类将实现创建线程所需的 Runnable 接口 . 在 ThreadClass 中,也需要实现 run() 方法( Runnable 中的唯一方法) . 很明显,摆脱 ThreadClass 会更有效率,这正是匿名内部类存在的原因 .

    请查看以下代码

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

    此代码替换了最顶层示例中对 task 的引用 . Thread() 构造函数中的匿名内部类不是具有单独的类,而是返回一个未命名的对象,该对象实现 Runnable 接口并覆盖 run() 方法 . 方法 run() 将包含执行线程所需工作的语句 .

    回答关于匿名内部类是否是Java的优点之一的问题,我不得不说我不太确定,因为我现在不熟悉许多编程语言 . 但我可以说它绝对是一种更快捷,更简单的编码方法 .

    References: Sams Teach Yourself Java in 21 Days Seventh Edition

相关问题