Bloch Effective Java - favor static classes over nonstatic - how many instances?

前端 未结 4 1241
有刺的猬
有刺的猬 2021-01-17 12:41

I want to know how many instances of a static member class can be created by the enclosing class. I assume one only, but then the following extract from Bloch doesn\'t make

相关标签:
4条回答
  • 2021-01-17 13:09

    Yes, you can have many instances of the nested class, no matter that the nested class is static.

    When the nested class is static you can create instances of it without having an instance of the enclosing class, this is one of the benefits, and basically the main difference between static and non-static nested classes.

    Does this mean then that a static member class is not actually instantiated when the enclosing object is instantiated?

    It's instantiated when it's constructor is called. Not any different from non-static classes. The nested class itself is loaded by the JVM, when the code first accesses it. Again this is not any different when compared to other classes, I think (not 100% sure of this though, but you can test it yourself). So I think you're kind of mixing the terms "loading the class by the JVM" and "instantiating the class".

    Well in that case, what's the point of Map using a static member class for Entry? Why not just use an interface on the API?

    As said, it's easier to create instances of static nested classes. You don't need an enclosing instance which is sometimes (maybe most of the times) exactly what you want.

    See also:

    (1) Nested Classes

    (2) How can the JVM decide if a class is nested into another class?

    (3) Loading of a nested class by the JVM

    You can search for other references along these lines.
    The reference (2) seems advanced and kind of peripheral to your question.

    0 讨论(0)
  • 2021-01-17 13:16

    I think the Java team messed up the naming on this one. A static inner class (strictly speaking their correct name is "static nested class") is in no way different from an ordinary class except it has a fancy name (Something.MyClass instead of MyClass) and can be made private (i.e. not instantiable from other classes).

    In case of Map, it was solely chosen because the name Map.Entry makes it clear that Entry relates to Map. As you suggest, it would have been perfectly reasonable to just use an ordinary class for this. The only difference is you don't get to write Map.Entry.

    I think what they should have done is to use the syntax for "non-static" inner classes (i.e. just class in an enclosing class) for static nested classes, and instead invent a new keyword to create "non-static" inner classes, because it's these that behave different from normal classes. Maybe something like attached class. AFAIK the keyword static was chosen in order to avoid having too many reserved keywords, but I think it just encouraged confusion.

    0 讨论(0)
  • 2021-01-17 13:19

    what's the point of Map using a static member class for Entry?

    That's because, it makes the package structure logically correct.

    Why not just use an interface on the API?

    Now, this is a design discussion nobody would like to be dragged into.

    0 讨论(0)
  • 2021-01-17 13:23

    This is a common misinterpretation of the static keyword.

    When you use static with a variable it means there will be only one of these for all objects of this class or something like that.

    static Object thereWillBeOnlyOne = new Object();
    

    However, in the context of inner classes it means something completely different. A static inner class has no connection with an object of the enclosing class while a non-static inner class does.


    A static inner class:

    public class TrieMap<K extends CharSequence, V> extends AbstractMap<K, V> implements Map<K, V> {
    
      private static class Entry<K extends CharSequence, V> implements Map.Entry<K, V> {
    

    The Map.Entry class used by my TrieMap class does not need to refer to the object that created it so it can be made static to save the unnecessary reference.


    A non-static inner class:

    public final class StringWalker implements Iterable<Character> {
      // The iteree
      private final String s;
      // Where to get the first character from.
      private final int start;
      // What to add to i (usually +/- 1).
      private final int step;
      // What should i be when we stop.
      private final int stop;
    
      // The Character iterator.
      private final class CharacterIterator implements Iterator<Character> {
        // Where I am.
        private int i;
        // The next character.
        private Character next = null;
    
        CharacterIterator() {
          // Start at the start.
          i = start;
        }
    
        public boolean hasNext() {
          if (next == null) {
            if (step > 0 ? i < stop : i > stop) {
              next = s.charAt(i);
              i += step;
            }
          }
          return next != null;
        }
    

    The CharacterIterator inside a StringWalker object refers to the string to be iterated as s which only exists once in the StringWalker object. I can therefore create many iterators of a StringWalker and they all walk the same string.


    Why this weirdness?

    This seemingly illogical duality derives from the use of the static keyword in C.

    In C you can (or at least used to be able to) do:

    void doSomething () {
       static int x = 1;
    
       if ( x < 3 ) {
       } else {
       }
       x += 1;
    }
    

    and each time you called the function, x would be as you left it last time around - incremented in this case.

    The concept was that the static keyword indicated that the variable was scopefully enclosed by its enclosing block but semantically enclosed by its parent block. I.e. the above code was roughly equivalent to:

    int x = 1;
    void doSomething () {
       if ( x < 3 ) {
       } else {
       }
       x += 1;
    }
    

    but x was only allowed to be referenced inside the function.

    Take that concept forward into Java and things now make a little more sense. A static inner class behaves exactly like it was declared outside the class while a non-static inner bonds much more tightly to its enclosing instance - in fact it can refer to the instance directly.

    Also:

    class Thing {
       static Object thereWillBeOnlyOne = new Object();
    

    behaves much like

    Object thereWillBeOnlyOne = new Object();
    class Thing {
    

    if it were legal.

    Here endeth the lesson.

    0 讨论(0)
提交回复
热议问题