什么导致错误“类型Foo的封闭实例不可访问”,我该如何修复它?

360

I have the following code:

class Hello {
    class Thing {
        public int size;

        Thing() {
            size = 0;
        }
    }

    public static void main(String[] args) {
        Thing thing1 = new Thing();
        System.out.println("Hello, World!");
    }
}

我知道Thing没有任何作用,但是我的Hello, World程序在没有它的情况下也可以成功编译。只有我定义的类出现了问题。
并且它拒绝编译。在创建一个新的Thing时,我得到了No enclosing instance of type Hello is accessible.的错误信息。我猜可能是以下两种情况之一:
  1. 我存在系统级问题(无论是在DrJava还是我的Java安装中),或者
  2. 我对如何构建一个可运行的Java程序存在一些基本的误解。
你有什么想法吗?

可能是重复的问题,与Java内部类和静态嵌套类相关。 - Strelok
11个回答

553

static class Thing将使您的程序正常工作。

现在,您将Thing定义为内部类,这意味着它(根据定义)与特定的Hello实例相关联(即使它从未使用或引用它),这意味着如果没有特定的Hello实例在范围内,使用new Thing();是错误的。

如果您将其声明为静态类,则它是一个“嵌套”类,不需要特定的Hello实例。


1
这是否意味着,如果我实例化了“外部类”,即使我在任何地方都没有使用它,“非静态内部类”也会被创建? - mr5
1
不是的。每个内部类对象必须有一个父级,但一个父级可以有任意数量的子级,包括0个。 - jacobm

108
你已经将类Thing声明为非静态内部类。这意味着它必须与Hello类的实例相关联。
在你的代码中,你试图从静态上下文中创建Thing的一个实例。这就是编译器抱怨的原因。
有几种可能的解决方案。使用哪个解决方案取决于你想要实现什么目标。
  • Move Thing out of the Hello class.

  • Change Thing to be a static nested class.

    static class Thing
    
  • Create an instance of Hello before creating an instance of Thing.

    public static void main(String[] args)
    {
        Hello h = new Hello();
        Thing thing1 = h.new Thing(); // hope this syntax is right, typing on the fly :P
    }
    
最后一种解决方案(一个非静态嵌套类)是必需的,如果任何一个 Thing 实例依赖于 Hello 实例才有意义。例如,如果我们有以下代码:
public class Hello {
    public int enormous;

    public Hello(int n) {
        enormous = n;
    }

    public class Thing {
        public int size;

        public Thing(int m) {
            if (m > enormous)
                size = enormous;
            else
                size = m;
        }
    }
    ...
}

尝试创建一个Thing类对象的任何原始方法,例如:

Thing t = new Thing(31);

如果没有一个明显的enormous值来测试31,那将会是有问题的。需要Hello外部类的一个实例h来提供这个h.enormous值:

...
Hello h = new Hello(30);
...
Thing t = h.new Thing(31);
...

如果没有一个 Hello,那么这个 Thing 就没有意义。

有关嵌套/内部类的更多信息: 嵌套类 (Java 教程)


你的回答既全面又综合。即使对我来说感觉有些奇怪,语法也是正确的。 - boumbh
如果仍然出现错误,语法Thing thing1 <<HelloInstantiation>>.new Thing()是关键。我花了几分钟时间困惑于语法Thing thing1 new <<HelloInstantiation>>.Thing()。=P - nikodaemus
1
@skia.heliou 谢谢!我正在制作一个帮助类,静态变量(甚至是类)不仅可以消除范围,而且通常效率低下。需要提供运行时参数使得使用主方法很麻烦。这就是我所需要的,也正是我想要找到的。 - Abandoned Cart

27

好的...很多好的答案,但我想再加点。在Java中简要了解内部类——Java允许我们定义一个类在另一个类中,并且以这种方式嵌套类具有某些优势:

  1. 它可以将该类从其他类中隐藏(增加封装性)——尤其是如果只有包含它的类在使用该类时。在这种情况下,外部世界不需要知道它。

  2. 它可以使代码更易于维护,因为类是根据它们所需的地方逻辑分组在一起的。

  3. 内部类可以访问其包含类的实例变量和方法。

我们主要有三种类型的内部类

  1. 局部内部类
  2. 静态内部类
  3. 匿名内部类

一些重要的注意事项

  • 我们需要类对象来访问其中存在的局部内部类。
  • 静态内部类可以直接被访问,就像任何其他静态方法一样,在它所存在的同一类中。
  • 匿名内部类对外界和其所在类的其他方法或类都不可见,并且它只用于声明的点。

让我们尝试将上述概念实践一下_

public class MyInnerClass {

public static void main(String args[]) throws InterruptedException {
    // direct access to inner class method
    new MyInnerClass.StaticInnerClass().staticInnerClassMethod();

    // static inner class reference object
    StaticInnerClass staticInnerclass = new StaticInnerClass();
    staticInnerclass.staticInnerClassMethod();

    // access local inner class
    LocalInnerClass localInnerClass = new MyInnerClass().new LocalInnerClass();
    localInnerClass.localInnerClassMethod();

    /*
     * Pay attention to the opening curly braces and the fact that there's a
     * semicolon at the very end, once the anonymous class is created:
     */
    /*
     AnonymousClass anonymousClass = new AnonymousClass() {
         // your code goes here...

     };*/
 }

// static inner class
static class StaticInnerClass {
    public void staticInnerClassMethod() {
        System.out.println("Hay... from Static Inner class!");
    }
}

// local inner class
class LocalInnerClass {
    public void localInnerClassMethod() {
        System.out.println("Hay... from local Inner class!");
    }
 }

}

我希望这对大家有所帮助。请参考更多信息


1
建议改进:将第一个类命名为MyOuterClass,删除AnonymousClass周围的注释。 - Noumenon
如前所述,anonymousClass 语句无效,因为赋值将失败。 - Prasanna K Rao

12

Thing 是一个 内部类,它自动连接到一个 Hello 的实例。你会得到编译错误,因为没有 Hello 的实例可以连接。你可以最容易地通过将其更改为 静态嵌套类 来修复它,这样就没有连接了:

static class Thing

9

我们通过以下简单的例子来理解它。

这是因为它是非静态内部类。你需要外部类的实例。

 public class PQ {

    public static void main(String[] args) {

        // create dog object here
        Dog dog = new PQ().new Dog();
        //OR
        PQ pq = new PQ();
        Dog dog1 = pq.new Dog();
    }

    abstract class Animal {
        abstract void checkup();
    }

    class Dog extends Animal {
        @Override
        void checkup() {
            System.out.println("Dog checkup");

        }
    }

    class Cat extends Animal {
        @Override
        void checkup() {
            System.out.println("Cat Checkup");

        }
    }
}

4

在 Java 14 之前 你必须添加 static 关键字才能从静态上下文中访问类 Thing

class Hello {
    static class Thing {
        public int size;

        Thing() {
            size = 0;
        }
    }

    public static void main(String[] args) {
        Thing thing1 = new Thing();
        System.out.println("Hello, World!");
    }
}

Java 14+ 从Java 14开始,您可以使用内部记录类,它们是隐式静态的。因此,您将拥有以下代码:

class Hello {
    record Thing(int size) { }

    public static void main(String[] args) {
        Thing thing1 = new Thing(0);
        System.out.println("Hello, World!");
    }
}

注意,使用记录时size将是final - dan1st

0
在我的情况下,是因为多了一个 '}'。

0

你实际上不必将任何东西声明为静态的,但是你必须指定一些封闭实例,可以通过在同一类中隐式地指定,在 new 之前显式地指定,或者在构造函数中显式地指定。

class A {

    class B { 
        final A a = A.this;
    }

    class C extends B { }

    final B b = new B();
    final C c = new C();

}

class D extends A.B {

    public D(A a) {
        a.super();
    }

}

class E { 

    final A   a =     new A();
    final A.B b =   a.new B();
    final A.C c = b.a.new C();
    final D   d =     new D(a);

}

0
class Hello {
    class Thing {
        public int size;

        Thing() {
            size = 0;
        }
    }

    public static void main(String[] args) {
        
        PlayGround obj = new PlayGround();
        
        Thing obj2 = obj.new Thing();
        
        System.out.println("Hello, World!");
    }
}

2
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

0

朋友,试试这个:(你也可以称它为 Hello 而不是 Main)

class Thing {
  public int size;

    Thing() {
      size = 0;
    }
}


class Main {
  public static void main(String[] args) {
    Thing thing1 = new Thing();
      System.out.println("Hello, World!");
  }
}

这个想法的背后是你必须创建一个单独的类来包含静态void main(String [] args)方法。总结一下:你必须有一个类来创建你的对象,另一个类(在前一个类之外)中包含对象创建。如果你把它叫做Main,你应该有一个名为Main.java的文件。如果你想叫它Hello,那么你的文件必须命名为Hello.java。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接