1

So I thought I understood packages, but turns out I don't.

  • Classes inside a package: I have this folder structure: mypackage/mysubpackage. Inside mysubpackage folder I have 2 classes:

    package mypackage.mysubpackage;
    
    public class Class1 {...}
    

and

    package mypackage.mysubpackage;

    public class Class2 {...}

However, when I compile Class1 (which uses methods from Class2) using javac Class1.java inside the directory mypackage/mysubpackage, it can't see Class2:

Class1.java: error: cannot find symbol
    Class2 c = new Class2();
    ^
symbol:   class Class2
location: class Class1

It works fine if I run javac Class1.java in the directory that contains mypackage/mysubpackage. Shouldn't the compilation work inside mysubpackage folder?

  • Classes in another package: Now, I have another class with methods that I want to be accesible to all the subpackages, so I create a final Commons.java inside mypackage/commons:

    package mypackage.commons;
    
    public final class Commons {
    
        public static double method() {...}
        ...
    }
    

And then I update Class2 importing that class so that I can use its methods inside the class:

    package mypackage.mysubpackage;

    import mypackage.commons.*;

    public class Class2 {...}

Now it doesn't find the method I defined in the final class:

./mypackage/mysubpackage/Class2.java: error: cannot find symbol
        double var = method();
                        ^
symbol:   method method()
location: class Class2

Shouldn't if find it? I think I'm importing it correctly, the methods are static and the class is final. Why doesn't it recognize it?

Cheers!

Xirux Nefer
  • 830
  • 1
  • 7
  • 19
  • 3
    "using javac Class1.java" -- that's your mistake. You should be compiling as "javac package/subpackage/Class1.java". Or specify the appropriate classpath on the *javac*. – Hot Licks Sep 07 '14 at 14:05
  • Could you add your code please ? – Hugues Sep 07 '14 at 14:05
  • (Classes must be located in a place in the directory structure corresponding to their package.) – Hot Licks Sep 07 '14 at 14:07
  • @HotLicks Ok so that's why it works when I'm in the folder that contains them. I thought it will work inside the local folder as well. – Xirux Nefer Sep 07 '14 at 14:09
  • 1
    And if you want to be able to call a static method without naming the class you must import the class, not just it's package. – Hot Licks Sep 07 '14 at 14:09
  • @Hugues Sorry, I don't follow. What do you mean by "add your code"? I thought I had included enough code to understand what's going on. Can you be more specific please? – Xirux Nefer Sep 07 '14 at 14:10
  • @HotLicks I tried importing the class too, as `import mypackage.commons.Commons;` but still got an error, so I changed `Commons` for an asterisk. But that doesn't work either. – Xirux Nefer Sep 07 '14 at 15:11
  • @HotLicks looks like it works if I do the import AND call the method as `Commons.method()`. Which I find weird, because for Math functions for example, you can do Math.sqrt() without importing Math ¿?. I don't understand packages anymore, he he. – Xirux Nefer Sep 07 '14 at 16:12
  • If you import the *class* then you can call the method without naming the class (assuming there's no ambiguity). – Hot Licks Sep 07 '14 at 18:17

4 Answers4

2

Looks like your problem is with where you set your working directory when you launch the Java compiler from the command line.

I would recommend that you pick up an integrated development environment -- Eclipse is a good one. Using the IDE you run into no such problems. Here are the classes I just created in Eclipse, which compile correclty.

Commons

package com.example.packagecommons;

public class Commons {

     public static double method() {
        return 0;}
}

Class1

package com.example.packages;

public class Class1 {
    private Class2 c2;
    public Class1() {
        c2 = new Class2();
    }
}

Class2

package com.example.packages;

import com.example.packagecommons.Commons;
public class Class2 {
    private double initialValue;
    public Class2() {
        initialValue = Commons.method();
    }
    public double getValue() {
        return initialValue;
    }
}
ErstwhileIII
  • 4,829
  • 2
  • 23
  • 37
  • Interesting, so I should use three levels in the folder hierarchy? I'll try that. It's strange that you should still call the method as `Commons.method` though. Regarding the IDE, I have NetBeans in my Windows laptop, but it's being fixed, so I'm alone with an Ubuntu that is not mine and I don't want to install things on it. That's why I have to make this work in the CLI. Which it should work, on the other side. – Xirux Nefer Sep 07 '14 at 15:18
  • No .. you don't need three levels, that is just my own packaging structure for Java StackOverflow. You would want com..xxx probably though (to identify you if you share code). – ErstwhileIII Sep 07 '14 at 15:25
  • Ok, three level structure doesn't work either. Still doesn't recognize the method. I don't know why, `Commons.java` is `public final` and its methods are `public static`, and the folder hierarchy seems to be fine. – Xirux Nefer Sep 07 '14 at 15:27
  • You do put your code in a folder structure that matches your package names, right? – ErstwhileIII Sep 07 '14 at 15:47
  • Yeah, I say that in my question =) Also the class files match the class names and everything. – Xirux Nefer Sep 07 '14 at 15:49
2

Suppose your two classes Demo01 and Demo03 are in package pack1.subpack and your Demo02 is in pack2

So the hierarchy is like

  • someDrive/pack1/subpack/Demo01
  • someDrive/pack1/subpack/Demo03
  • someDrive/pack2/Demo02
  • someDrive/pack1/common/Demo04

where Demo01 is

package pack1.subpack;
import pack2.Demo02; // need to add this if calling class of different package
import pack1.common.Demo04; // if you are going to use Demo04 class in Demo01 class
public class Demo01 {

public void run() {
    System.out.println("--running Demo01-");
}
public static void main(String[] args){
    Demo01 demo01 = new Demo01();
    demo01.run();
    Demo02 demo02 = new Demo02();
    demo02.run();
    Demo03 demo03 = new Demo03();
    demo03.run();
    Demo04.run();
 }
}

Demo02 is

package pack2;
public class Demo02 {

public void run() {
    System.out.println("--running Demo02--");
}
}

Demo03 is

package pack1.subpack;   
public class Demo03 {

public void run() {
    System.out.println("--running Demo03--");
}
}

Demo04 is

package pack1.common;

public final class Demo04 {

public void run() {
    System.out.println("--running Demo04--");
}
}

Then just compile it using javac pack1/subpack/Demo01.java

and execute it using java pack1/subpack.Demo01

SparkOn
  • 8,806
  • 4
  • 29
  • 34
  • Quite a comprehensive example thank you. That solves the first problem, but I still can't make my second example work, although my folder hierarchy is right and I think the invocation is right as well. – Xirux Nefer Sep 07 '14 at 15:22
  • check the edit of Demo01(import) and Demo04 it must clarify your confusion – SparkOn Sep 07 '14 at 15:31
  • Thank you for the edit. That's exactly what I have, a `package pack1.subpack;` and then an `import pack1.common.Demo04;`, then I call the `Demo04` method inside `Demo01` and it throws an error. My `Demo04` class is `final` and its method `run()` is `static`, so I don't know what's going wrong. – Xirux Nefer Sep 07 '14 at 15:40
  • it works fine check Demo04 and Demo01 how to call run() of Demo04 – SparkOn Sep 07 '14 at 16:13
2

I know this thread is old but I'd like to clarify things so as to help future viewers.

Your first question basically is, how does the Java run-time system know where to look for packages that you create? Remember these 3 rules (one of them must apply):

  1. Your main package must be in a subdirectory of the current working directory to be found.
  2. You can specify a directory path or paths by setting the CLASSPATH environmental variable.
  3. You can use the -classpath option with java and javac to specify the path to your classes when you are executing your code via the terminal/cmd.

To answer your first question, you are executing your code from mypackage/mysubpackage. For Java run-time to recognise Class2, you must execute from mypackage.

Coming to the second question, when you import all the contents of a package using *, you need to refer to static class members by explicitly writing the class name before them, as Java does not know which class in the package you are referring to. Hence, in your code, you must write Commons.method() instead of just method(). If you do not want to prefix the name of the class time and again, you can explicitly import the specific class you want. In your case, this would be mypackage.commons.Commons. Then you can call method() directly (provided it is static).

Rohan Saxena
  • 3,133
  • 2
  • 16
  • 34
0
sh$ cd package/subpackage
sh$ javac Class1.java

Will lead to an error as the compiler will try to locate Class2 in the package/subpackage subdirectory of the current directory.

You have to compile that way:

sh$ javac package/subpackage/Class1.java

Here is a complete working example:

sh$ cat pkg/subpackage/Class1.java
package pkg.subpackage;

import pkg.commons.Class2;

public class Class1 {
    public static void main(String args[]) {
        Class2.doSomething();
    }
}

sh$ cat pkg/commons/Class2.java 
package pkg.commons;

public class Class2 {
    public static void doSomething() {
        System.out.println("hello");
    }
}

sh$ javac pkg/subpackage/Class1.java
sh$ java pkg.subpackage.Class1
hello
Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125
  • 1
    "BTW package is a very bad package name." Well, I thought it was pretty obvious that those are names used for the sake of the example. Neither am I naming my classes "Class1" and "Class2", nor am I naming my method "method()". – Xirux Nefer Sep 07 '14 at 14:46
  • @XiruxNefer I understand that. But even for an example it is unsuitable as it prevents proper compilation, whereas `Class1`, `method` and so on are valid identifiers. I removed that from the answer. – Sylvain Leroux Sep 07 '14 at 14:50
  • 1
    Fair enough. My apologies. – Xirux Nefer Sep 07 '14 at 15:03