Java: Anonymous Inner Class Instance Initializer Access to Outer Class Private Members

StackOverflow https://stackoverflow.com/questions/23085736

  •  04-07-2023
  •  | 
  •  

Pergunta

I have a situation where I can't change the main API of a set of classes, but I need to construct a new instance from archival data, including setting a large number of private fields.

My idea thus far was to create a static factory method that would produce my instance from the archival, like so:

Version 1:

public class Item {
    private Long id;

    public static Item fromArchived(final ArchivedItem archived) {
        return new Item(){{
            this.id = archived.getId();
        }};
    }
}

IntelliJ IDEA gives me no red lines, I didn't have any idea there was an issue until I ran my maven build which yields:

error: id has private access in Item

In contrast, this works:

Version 2:

public class Item {
    private Long id;

    public static Item fromArchived(final ArchivedItem archived) {
        Item item = new Item(){{
//            this.id = archived.getId();
        }};

        item.id = archived.getId();

        return item;
    }
}

I've been reviewing my core Java knowledge, and the only vague guess I have as to why Version 1 doesn't work is that the instance initializer (double brace initialization) somehow occurs in the static context, which doesn't really make sense either. Thoughts?

EDIT

I think the initial form of the question was vague. The answer I'm looking for is one which explains why I have visibility to the parent's private id field in the body of the static method but I lose visibility to it in the body of the instance initializer of the anonymous subclass inside that same static method.

Foi útil?

Solução

The other answers have been accurate, but here's my take.

public class Item {
    private Long id;

    public static Item fromArchived(final ArchivedItem archived) {
        return new Item(){{
            this.id = archived.getId();
        }};
    }
}

Here the static method generates an anonymous class that extends Item. Item$id will be hidden from this anonymous class.

The following snippet is equivalent to the previous, but as a named nested class that exposes a constructor method rather than a method that calls a constructor.

public class Item {
    private Long id;

    class FromArchived extends Item {
        public FromArchived(final ArchivedItem archived) {
            this.id = archived.getId();
        }
    }
}

The visibility modifiers are enforced consistently whether the access is from a named, nested or anonymous class.

Finally, this following snippet should work as while inside a class, you have full access to all the private fields & methods of that class.

public class Item {
    private Long id;

    public static Item fromArchived(final ArchivedItem archived) {
        Item item = new Item();
        item.id = archived.getId();
        return item;
    }
}

This is true whether you're in a constructor, method or static method.

Outras dicas

you have been creating anonymous inner class that extends the Item class.private variables are accessible from within the same class where those declared.So you are not allowed to access Item class private variables in anonymous inner class

In your first example, this refers to anonymous (inner) Item, not the outer, that's why you get error message.

With the construct below you create Item subclass with instance initialization block inside.

return new Item() { // equivalent to: class AnonymousItem extends Item
    {
        this.id = archived.getId();
    }
};

Subclasses do not have access to private fields of parent classes, so compiler generates error for you.

Instead of writing this.id = ... you may write ((Item) this).id = ... or super.id = ....

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top