Why use method local abstract inner classes
Asked Answered
W

10

26

One of the legal modifiers you can use with method local inner classes is abstract.

For example:

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

Is there any situation where you would actually use this?

You have to know this for the SCJP exam.

Wrapper answered 29/4, 2011 at 10:22 Comment(0)
U
14

The are some invalid assumptions in the original question. That something is legal/valid Java doesn't mean that it is something that you need to use, or need to know.

I can't recall that the SCJP contains odd corner case questions.

I tried to come up with a case where I would have used an abstract class declared in a method, but everything looks very odd, and reeks of bad design. Here's however a code example that I came up with (still bad code design IMHO)

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
    }

}
Ulani answered 7/5, 2011 at 9:20 Comment(3)
+1 This looks like the best example. Still quite bad, since the method gets too long and since the behavior should better be placed into the enum instead.Sella
I totally agree, the design smells big time, and that's probably why I've never seen anyone do that during my 15 years of programming Java.Ulani
Agreed. Additionally, for the bad programmers it's too high and the good ones avoid it.Sella
A
10

I can think only in this case

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

But I can't imagine real usage

Anesthetist answered 29/4, 2011 at 10:42 Comment(0)
S
7

IMHO, this feature has NO real use. There's a couple of possible abuses, but there are many other ways to write bad code, you needn't learn this one. :D

Whenever you try to make use of an abstract method-local class, you need to define at least two concrete method-inner classes. This means you end up with a method containing at least three classes, the method gets quite long and that's quite a bad style.

You have to know this for the SCJP exam.

I really hope not. Method-local inner classes are already useless enough to be considered a corner case (you should understand them but probably never use them).

IMHO, a person asking this in an exam misunderstood Java badly. There can't be accessibility modifiers on a local class since (lacking method literals) the class can't be accessed from the outside anyway. There can be abstract and final modifiers, since there's no reason to forbid them. There are good reasons to allow them: orthogonality and the Principle of least astonishment.

Sella answered 9/5, 2011 at 21:8 Comment(3)
The only reason it is on the SCJP so you know what modifiers can be applied to method local inner classes. So for example if a class had a private modifier you would have to know it wouldn't compile.Wrapper
@Gordon: Maybe..., but putting a modifier on a method-local class just makes no sense (as you can't use it outside anyway). Knowing you can't do it doesn't help you, since you need to be quite misguided before you attempt to. Knowing what the compiler does in strange corner cases is useless, either you have the compiler and try it out or you don't have the compiler and don't need to know. There's a lot of complicated stuff in Java which is much much more useful to know.Sella
Unfortunately situation with the SCJP exam you have to act as a complier. There is a ridiculous amount of stuff you need to know that would be immediately picked up a complier.Wrapper
R
6

Is there any situation where you would actually use this?

  1. Let S1 denote all situations in which you need an abstract class.

  2. Let S2 denote all situations in which you need a local class.

  3. The answer to your question can be found by examining S1 ∩ S2

Related questions:


Clarification: My point is that the two features (abstract classes and local classes) are two completely orthogonal features of the language. Understanding when each feature is useful is the key to understanding when they are both useful at the same time.

Rus answered 29/4, 2011 at 10:40 Comment(3)
-1 I can't see any real value here - this way you could answer nearly any question without providing any information. Moreover, the intersection seems to be quite empty.Sella
If you find the intersection empty, I believe the question has been answered. (Clarified my answer a bit.)Rus
That's better. Actually, I don't think your answer is bad, it's just IMHO not good enough for that many upvotes (at least when compared to the much more concrete answers). +1 to the comment (also for mentioning orthogonality).Sella
K
2

You can get the use here http://java-questions.com/InnerClass_interview_questions.html

which says

The inner class declared inside the method is called method local inner class. Method local inner class can only be declared as final or abstract. Method local class can only access global variables or method local variables if declared as final

ie You can declare the static variables in the inner call and use them in the methods.

EDIT: Why abstract:

Because if you dont want to create the objects of the inner class. If you create the object in the method then it will be stored in the heap and it is not freed even if the method execution completes as there might be an external reference for this object when it is returned from the method.

So it depends on whether you want to create an instance or not. If you want to create then use final modifier.

Kynthia answered 29/4, 2011 at 10:27 Comment(2)
That doesn't explain why the inner class in the example is abstractFerula
You don't have to declare an class abstract to not instantiate it. The important part is, that you want to subclass it, and possibly with different implementations.Fabrianna
T
0

the only real use I can imagine is for nodes in a data structure

that way you can differentiate methods from sentinel nodes and normal data nodes which can be really handy in recursive algorithms and you don't have to null check each time

Transparent answered 7/5, 2011 at 21:43 Comment(0)
H
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);
    }
}

You can use it like this.

Haemoglobin answered 12/5, 2011 at 9:47 Comment(0)
M
0

No, there is no good use for abstract classes (or classes in general) inside methods.

It would only make sense if only that particular method would need that particular class and would also implement it. Actually having that situation maybe happens once in trillions of methods you write.

Minerva answered 12/5, 2011 at 14:33 Comment(0)
D
0

Check out the section titled "Hierarchies of Inner Classes" on this page.

The gist is that you can treat the inner class as just another abstract member that needs to be overridden/implemented. I don't necessarily agree with it (I would probably just define the inner class separately), but I've seen things like this in the wild.

Here's their example code:

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;
      }
   }
}
Drub answered 12/5, 2011 at 22:57 Comment(0)
H
0

I think it can be useful to reduce the scope of methods in certain conditions.

For exemple, I use it in unit tests. Sometimes you need an utility method to reduce the verbosity of a test. But this utility method may be related to the current test dataset, and can't be reused outside of this test.

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

In this real-life exemple, I could have done a regular inner class, but someone could have been tempted to reuse it in other tests, while it was not designed to.

By the way, you will notice the ability to "capture" the dataset build in the test directly inside the utility class. Using a regular inner class, it couldn't work without creating the test specific dataset outside the test too... so you end up with a lot of things shared with other tests, while they are used (should be used) by only one.


In the end, I don't think a feature permitting to reduce the visibility is useless.

You can build a perfectly working application without using encapsulation at all, and can argue the same thing, saying the private modifier is useless...

But yes, the private modifier is certainly more useful than method local innerclasses ;)

Housing answered 15/2, 2013 at 18:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.