为什么使用方法本地抽象内部类

时间:2011-04-29 10:22:23

标签: java abstract-class inner-classes

可以与方法本地内部类一起使用的合法修饰符之一是abstract。

例如:

public class Outer {
    public void method(){
        abstract class Inner{
        }
    }
}

有没有你真正使用它的情况?

你必须知道SCJP考试。

10 个答案:

答案 0 :(得分:12)

原始问题中的一些无效假设。某些东西是合法/有效的Java并不意味着它是您需要使用或需要知道的东西。

我不记得SCJP包含奇怪的案例问题。

我试图提出一个案例,我将使用在方法中声明的抽象类,但是一切看起来都很奇怪,并且设计糟糕。 然而,这是我提出的代码示例(仍然是糟糕的代码设计恕我直言)

public class BatchExecutor {

    public static enum ResultNotification {
        JMS,
        MAIL
    };

    public Runnable createRunnable(ResultNotification type) {
        abstract class Prototype implements Runnable {
            public void run() {
                performBusinessLogic();
                publishResult();
            }

            abstract void publishResult();
        }

        switch (type) {
            case JMS: {
                return new Prototype() {
                    void publishResult() {
                        //Post result to JMS
                    }
                };
            }
            case MAIL: {
                return new Prototype() {
                    void publishResult() {
                        //Post result to MAIL
                    }
                };
            }
        }
        return null;
    }

    private void performBusinessLogic() {
        //Some business logic
    }

}

答案 1 :(得分:9)

我只能在这种情况下思考

class Outer {
    public void method() {
        abstract class A {
            void bar(){}
            abstract void foo();
        }
        class B extends A {
            @Override
            void foo() {
            }
        }
        final class C extends A {
            @Override
            void foo() {
            }
        }
        A a1 = new B();
        A a2 = new C();
    }
}

但我无法想象真正的用法

答案 2 :(得分:7)

  

您是否有实际使用此功能的情况?

  1. 设S 1 表示您需要抽象类的所有情况。

  2. 设S 2 表示您需要本地课程的所有情况。

  3. 通过检查S 1 ∩S 2

  4. ,可以找到问题的答案。

    相关问题:


    澄清:我的观点是这两个功能(抽象类本地类)是该语言的两个完全正交的功能。了解每个功能何时有用是了解它们何时同时有用的关键。

答案 3 :(得分:6)

恕我直言,这个功能没有实际用途。有一些可能的滥用,但还有许多其他方法来编写错误的代码,你不需要学习这个。 :d

每当您尝试使用抽象方法本地类时,您需要至少定义两个具体的方法 - 内部类。这意味着你最终会得到一个至少包含三个类的方法,这个方法会很长,并且风格很糟糕。

  

你必须知道SCJP考试。

我真的希望不是。方法本地内部类已经无用了被认为是一个极端情况(你应该理解它们但可能永远不会使用它们)。

恕我直言,一个在考试中提出这个问题的人误解了Java。在本地类上不能有可访问性修饰符,因为(缺少方法文字)无论如何都无法从外部访问类。可以有abstractfinal修饰符,因为没有理由禁止它们。有充分的理由允许他们:orthogonalityPrinciple of least astonishment

答案 4 :(得分:1)

您可以在此处使用http://java-questions.com/InnerClass_interview_questions.html

在方法内声明的内部类称为方法本地内部类。方法本地内部类只能声明为final或abstract。方法本地类只能在声明为final

时访问全局变量或方法局部变量

即您可以在内部调用中声明静态变量,并在方法中使用它们。

编辑:为何抽象:

因为如果你不想创建内部类的对象。如果在方法中创建对象,那么它将存储在堆中,即使方法执行完成也不会释放它,因为当从该方法返回时,该对象可能存在外部引用。

因此,这取决于您是否要创建实例。如果您想创建,请使用最终修饰符。

答案 5 :(得分:0)

我能想到的唯一真正用途是数据结构中的节点

通过这种方式,您可以将方法与标记节点和普通数据节点区分开来,这在递归算法中非常方便,并且您不必每次都进行空检查

答案 6 :(得分:0)

package dto;

public class Outer {

    public void method(int x, int y){
        abstract class Inner{
            abstract void performAction(int x,int y);
        }
        class InnnerA extends Inner{

            @Override
            void performAction(int x,int y) {
                int z =x+y;
                System.out.println("addition :" + z);

            }

        }
        class InnnerB extends Inner{

            @Override
            void performAction(int x,int y) {
                System.out.println("multiply :"+x*y);

            }

        }
        Inner inner1 = new InnnerA();
        inner1.performAction(x,y);
        Inner inner2 = new InnnerB();
        inner2.performAction(x,y);
    }
    public static void main(String args[]){
        Outer outer = new Outer();
        outer.method(10,20);
    }
}

你可以像这样使用它。

答案 7 :(得分:0)

不,在方法内部没有很好地使用抽象类(或一般类)。

只有那个特定的方法需要那个特定的类并且也会实现它才有意义。实际上,这种情况可能会发生在您编写的数万亿方法中。

答案 8 :(得分:0)

查看this page上标题为“内部类的层次结构”的部分。

要点是,您可以将内部类视为另一个需要重写/实现的抽象成员。我不一定同意它(我可能只是单独定义内部类),但我在野外看到过这样的事情。

这是他们的示例代码:

public abstract class BasicMonitorScreen {
   private Dimension resolution;

   public BasicMonitorScreen(final Dimension resolution) {
      this.resolution = resolution;
   }

   public Dimension getResolution( ) {
      return this.resolution;
   }

   protected abstract class PixelPoint {
      private int x;

      private int y;

      public PixelPoint(final int x, final int y) {
         this.x = x;
         this.y = y;
      }

      public int getX( ) {
         return x;
      }

      public int getY( ) {
         return y;
      }
   }
}

public class ColorMonitorScreen extends BasicMonitorScreen {
   public ColorMonitorScreen(final Dimension resolution) {
      super(resolution);
   }

   protected class ColorPixelPoint extends PixelPoint {
      private Color color;
      public ColorPixelPoint(final int x, final int y, final Color color) {
         super(x, y);
         this.color = color;
      }

      public Color getColor( ) {
         return this.color;
      }
   }
}

答案 9 :(得分:0)

我认为在某些条件下减少方法的范围会很有用。

例如,我在单元测试中使用它。有时您需要一种实用方法来降低测试的详细程度。但是这种实用方法可能与当前的测试数据集有关,并且不能在该测试之外重复使用。

  @Test
  public void facetting_is_impacted_by_filtering() {
    // given
    String userId = "cd01d6b08bc29b012789ff0d05f8e8f1";
    DocumentSolrClient client = solrClientsHolder.getDocumentClient(userId);
    //
    final SolrDocument doc1 = createDocument(userId);
    doc1.setAuthorName("AuthorName1");
    doc1.setType("Type1");
    doc1.setUserTags(Arrays.asList("UserTag1", "UserTag1bis","UserTag1bisbis"));
    doc1.setSenderTags(Arrays.asList("SenderTag1", "SenderTag1bis"));
    doc1.setCreationDate( new Date(EnumDateRange.CURRENT_DAY.getBegin().getTime()+1000) );
    doc1.setLocation(DocumentLocation.INBOX);
    client.index(doc1);
    //
    final SolrDocument doc2 = createDocument(userId);
    doc2.setAuthorName("AuthorName2");
    doc2.setType("Type2");
    doc2.setUserTags(Arrays.asList("UserTag2"));
    doc2.setSenderTags(Arrays.asList("SenderTag2"));
    doc2.setCreationDate( new Date(1000) ); // cree il y a tres longtemps
    doc2.setLocation(DocumentLocation.SAFE);
    client.index(doc2);
    //
    final List<DateRange> facettedRanges = Arrays.<DateRange>asList(
            EnumDateRange.CURRENT_DAY,
            EnumDateRange.CURRENT_YEAR,
            EnumDateRange.BEFORE_CURRENT_YEAR
    );
    class TestUtils {
      ApiSearchRequest baseFacettingRequest(String userId) {
        ApiSearchRequest req = new ApiSearchRequest(userId);
        req.setDocumentTypeFacets(true);
        req.setSenderNameFacets(true);
        req.setSenderTagsFacets(true);
        req.setUserTagsFacets(true);
        req.addDateCreationFacets(facettedRanges);
        return req;
      }
      void assertDoc1FacettingResult(ApiSearchResponse res) {
        assertThat(res.getDocuments().size()).isEqualTo(1);
        assertThat(res.getDocumentTypeFacets().get().getCounts()).hasSize(1);
        assertThat(res.getSenderNameFacets().get().getCounts()).hasSize(1);
        assertThat(res.getSenderTagsFacets().get().getCounts()).hasSize(2);
        assertThat(res.getUserTagsFacets().get().getCounts()).hasSize(3);
        assertThat(res.getDateCreationFacets().get().getCounts()).isEqualTo( computeExpectedDateFacettingResult( Arrays.asList(doc1),facettedRanges) );
      }
      void assertDoc2FacettingResult(ApiSearchResponse res) {
        assertThat(res.getDocuments().size()).isEqualTo(1);
        assertThat(res.getDocumentTypeFacets().get().getCounts()).hasSize(1);
        assertThat(res.getSenderNameFacets().get().getCounts()).hasSize(1);
        assertThat(res.getSenderTagsFacets().get().getCounts()).hasSize(1);
        assertThat(res.getUserTagsFacets().get().getCounts()).hasSize(1);
        assertThat(res.getDateCreationFacets().get().getCounts()).isEqualTo( computeExpectedDateFacettingResult( Arrays.asList(doc2),facettedRanges) );
      }
    }
    TestUtils utils = new TestUtils();
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // when
    ApiSearchRequest req = utils.baseFacettingRequest(userId);
    ApiSearchResponse res = documentSearchService.search(req);
    // then
    assertThat(res.getDocuments().size()).isEqualTo(2);
    assertThat(res.getDocumentTypeFacets().get().getCounts()).hasSize(2);
    assertThat(res.getSenderNameFacets().get().getCounts()).hasSize(2);
    assertThat(res.getSenderTagsFacets().get().getCounts()).hasSize(3);
    assertThat(res.getUserTagsFacets().get().getCounts()).hasSize(4);
    assertThat(res.getDateCreationFacets().get().getCounts()).isEqualTo( computeExpectedDateFacettingResult( Arrays.asList(doc1,doc2),facettedRanges) );
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // when
    req = utils.baseFacettingRequest(userId);
    req.addLocation(DocumentLocation.SAFE);
    res = documentSearchService.search(req);
    // then
    utils.assertDoc2FacettingResult(res);
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // when
    req = utils.baseFacettingRequest(userId);
    req.addUserTag("UserTag1");
    res = documentSearchService.search(req);
    // then
    utils.assertDoc1FacettingResult(res);
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // when
    req = utils.baseFacettingRequest(userId);
    req.addSenderTag("SenderTag2");
    res = documentSearchService.search(req);
    // then
    utils.assertDoc2FacettingResult(res);
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // when
    req = utils.baseFacettingRequest(userId);
    req.setDocumentType("Type1");
    res = documentSearchService.search(req);
    // then
    utils.assertDoc1FacettingResult(res);
  }

在这个现实生活中,我可以做一个常规的内部类,但有人可能会试图在其他测试中重复使用它,而它不是为了设计的。

顺便说一句,您会注意到能够直接在实用程序类中“捕获”测试中的数据集构建。使用常规内部类,如果没有在测试之外创建测试特定数据集,它就无法工作......所以你最终会得到很多与其他测试共享的东西,而它们只被使用了一部分(应该被使用) 。


最后,我认为不允许降低能见度的功能是无用的。

你可以在不使用封装的情况下构建一个完美的应用程序,并且可以争论相同的事情,说私有修饰符是无用的......

但是,是的,私有修饰符肯定比方法本地内部类更有用;)