Cannot use class in jar's root when my file is part of a package but when my file is not part of a package, I can

huangapple go评论52阅读模式
英文:

Cannot use class in jar's root when my file is part of a package but when my file is not part of a package, I can

问题

I cannot use a class in jar's root when my file is part of a package but when my file is not part of a package, I can.

Possibly another way to describe the problem: A java file in the default package can refer to a class in the jar's unnamed package (class in the root of jar) but a java file in a named package cannot.

I've created a bash script to reproduce the problem.

build the jar

#!/bin/bash

# Create directory structure
mkdir -p projA/src/java/org/example
mkdir -p projA/out/artifacts

# Create Java source files
cat > projA/src/java/Class2.java << EOF
public class Class2 {
    // Empty class
}
EOF

cat > projA/src/java/org/example/Class1.java << EOF
package org.example;

public class Class1 {
    // Empty class
}
EOF

# Compile Java files
find projA/src/java -name "*.java" > sources.txt
javac -d projA/target @sources.txt

# Create JAR file
jar cf projA/out/artifacts/projA.jar -C projA/target .

# View contents of JAR file
tar tf projA/out/artifacts/projA.jar

use the jar

#!/bin/bash

# Create directory structure
mkdir -p ProjB/src/java/org/fred

# Create Java source files
cat > ProjB/src/java/Main2.java << EOF
public class Main2 {
    public static void main(String[] args) {
        Class2 class2 = new Class2();
    }
}
EOF

cat > ProjB/src/java/org/fred/Main1.java << EOF
package org.fred;

import org.example.Class1;

public class Main1 {
    public static void main(String[] args) {
        Class1 class1 = new Class1();
        // Class2 class2 = new Class2();  // <--- UNCOMMENT THIS TO REVEAL PROBLEM
    }
}
EOF

# Compile Java files
javac -d ProjB/target -cp projA/out/artifacts/projA.jar ProjB/src/java/*.java ProjB/src/java/org/fred/*.java

# Run the classes
java -cp ProjB/target:projA/out/artifacts/projA.jar Main2
java -cp ProjB/target:projA/out/artifacts/projA.jar org.fred.Main1

After running these scripts in a fresh directory, everything should compile, build, and run ok. Now uncomment the line with // <--- UNCOMMENT THIS TO REVEAL PROBLEM and run the second script again. Notice the compiler error

ProjB/src/java/org/fred/Main1.java:12: error: cannot find symbol
        Class2 class2 = new Class2();

Why can't I refer to Class2 - it's clearly in the root of the jar. I can refer to it successfully from ProjB/src/java/Main2.java but not from ProjB/src/java/org/fred/Main1.java?

Interestingly, I don't need to use an import statement when I'm referring to classes in the jar unnamed package (class in the root of jar) from a java file in the default package.

英文:

I cannot use a class in jar's root when my file is part of a package but when my file is not part of a package, I can.

Possibly another way to descibe the problem: A java file in the default package can refer to a class in the jar's unnamed package (class in the root of jar) but a java file in a named package cannot.

I've created a bash script to reproduce the problem.

build the jar

#!/bin/bash

# Create directory structure
mkdir -p projA/src/java/org/example
mkdir -p projA/out/artifacts

# Create Java source files
cat &gt; projA/src/java/Class2.java &lt;&lt; EOF
public class Class2 {
    // Empty class
}
EOF

cat &gt; projA/src/java/org/example/Class1.java &lt;&lt; EOF
package org.example;

public class Class1 {
    // Empty class
}
EOF

# Compile Java files
find projA/src/java -name &quot;*.java&quot; &gt; sources.txt
javac -d projA/target @sources.txt

# Create JAR file
jar cf projA/out/artifacts/projA.jar -C projA/target .

# View contents of JAR file
tar tf projA/out/artifacts/projA.jar

use the jar

#!/bin/bash

# Create directory structure
mkdir -p ProjB/src/java/org/fred

# Create Java source files
cat &gt; ProjB/src/java/Main2.java &lt;&lt; EOF
public class Main2 {
    public static void main(String[] args) {
        Class2 class2 = new Class2();
    }
}
EOF

cat &gt; ProjB/src/java/org/fred/Main1.java &lt;&lt; EOF
package org.fred;

import org.example.Class1;

public class Main1 {
    public static void main(String[] args) {
        Class1 class1 = new Class1();
        // Class2 class2 = new Class2();  // &lt;--- UNCOMMENT THIS TO REVEAL PROBLEM
    }
}
EOF

# Compile Java files
javac -d ProjB/target -cp projA/out/artifacts/projA.jar ProjB/src/java/*.java ProjB/src/java/org/fred/*.java

# Run the classes
java -cp ProjB/target:projA/out/artifacts/projA.jar Main2
java -cp ProjB/target:projA/out/artifacts/projA.jar org.fred.Main1

After running these scripts in a fresh directory, everything should compile, build and run ok. Now uncomment the line with // &lt;--- UNCOMMENT THIS TO REVEAL PROBLEM and run the second script again. Notice the compiler error

ProjB/src/java/org/fred/Main1.java:12: error: cannot find symbol
        Class2 class2 = new Class2();

Why can't I refer to Class2 - its clearly in the root of the jar. I can refer to it successfully from ProjB/src/java/Main2.java but not from ProjB/src/java/org/fred/Main1.java?

Interestingly, I don't need to use an import statement to when I'm referring to classes in the jar unnamed package (class in the root of jar) from a java file in the default package.

答案1

得分: 1

所有的类都位于一个包中。即使没有包声明的类也是如此;这些被视为在_未命名包_中。这不是俚语;这是JLS(Java语言规范)中明确使用的术语。

根据JLS,从未命名的包中__不可能__导入。import Class2; 是语法错误;其中必须至少有一个点,而且没有关键字可以指示“未命名包,请”。

JLS还指出,任何源文件都会自动导入其所在的包。例如,给定:

package com.foo;

public class Hello {}

这就好像文件中有 import com.foo.*;:可以直接引用位于com.foo包中的任何类,除非明确导入了该名称,否则java将假定您指的是您包中的那个类(命名导入优先于星号导入;这是显而易见的,并在JLS中有规定)。

对于未命名包,也是同样的情况:位于未命名包中的任何内容都会自动且静默地星号导入未命名包。当然,无法编写执行此操作的导入语句。

这些步骤得出以下事实:

无法__引用__未命名包中的内容。除非来自未命名包中的其他类型,因为它们星号导入未命名包。

因此:

> 默认包中的Java文件可以引用jar文件的未命名包中的类(jar根目录中的类),但具有命名包的Java文件不能。

是的。正确。而且无法修复。

什么??

未命名包__仅__用于一次性项目和极其简单的应用程序,不希望以任何方式干扰包。这就是全部。一旦在项目中的任何地方写了package,那一刻__每个__源文件都应该获得一个包声明。

你可能会觉得这不方便,但这无法解决;JLS 要求 Java编译器以这种方式工作。

英文:

All classes are in a package. Even ones without a package statement; these are then considered to be in the unnamed package. That is not a slang term; that is the explicit term used in the JLS (Java Language Specification).

As per the JLS, it is not possible to import from the unnamed package. import Class2; is a syntax error; there has to be at least one dot in there, and no keyword exists to indicate 'the unnamed package, please'.

The JLS also states that any source file trivially star-imports its own package. e.g, given:

package com.foo;

public class Hello {}

It is exactly as if import com.foo.*; was in that file: Any class that is in the com.foo package can just be referred to, and unless that name is explicitly imported, java will assume you meant the one in your package (named imports win over star imports; this is obvious, and specced in the JLS).

The same applies to the unnamed package: Anything in the unnamed package therefore automatically and silently star-imports the unnamed package. There is no way to write an import statement that does that, of course.

These steps add up the following fact:

It is impossible to refer to things in the unnamed package. Except from other types in the unnamed package, because they star-import the unnamed package.

Hence:

> A java file in the default package can refer to a class in the jar's unnamed package (class in the root of jar) but a java file in a named package cannot.

Yes. True. And not fixable.

What??

The unnamed package is meant solely for throwaway projects and for extremely simple apps that do not want to mess with packages in any way. That's all. The moment you write package anywhere in the project, that is the moment every source file should gain a package statement.

You may find this inconvenient, but there is no fix for this; the JLS requires that java compilers work this way.

huangapple
  • 本文由 发表于 2023年5月22日 09:00:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76302523.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定