skip to Main Content

In a Java shebang script I get this crazy error: can't find main(String[]) method in class: gomad.MyKotlin and the 1st question I ask to myself is: "WHY on Earth does it look for the main() method in that class?" (UPD Well, this does reproduce when java compiles-and-runs a single-file program.)

#!/usr/bin/java --source 11

package gomad;

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static gomad.MyKotlin.*;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world!");
        int[] x = MyArrays.sorted(3, 14, 15, 92, 6, 53, 58, 97, 93, 2, 38, 4, 62, 6, 43);
        System.out.println(Arrays.stream(x).boxed().collect(Collectors.toList()));
    }
}

class MyKotlin { // the idea of also() comes from Kotlin
    static <T> T also(T obj, Consumer<T> action) {
        action.accept(obj);
        return obj;
    }
}

class MyArrays {
    static int[] sorted(int ... xs) {
        return also(xs, Arrays::sort);
    }
}

In the case of this script, the message is error: can't find main(String[]) method in class: gomad.MyKotlin.

When this program is run from IntelliJ Idea with she-bang commented out, everything works.

It is important to note that when this program is run as java Main.java, the same senseless error: can't find main(String[]) method in class: gomad.MyKotlin appears.
But If you try to run it as javac gomad/Main.java && java gomad.Main, you get the expected result, no error.

$ /usr/lib/jvm/java-21-openjdk-amd64/bin/java Main.java
error: can't find main(String[]) method in class: gomad.MyKotlin
$ /usr/lib/jvm/java-21-openjdk-amd64/bin/java -version
openjdk version "21.0.2" 2024-01-16
OpenJDK Runtime Environment (build 21.0.2+13-Ubuntu-122.04.1)
OpenJDK 64-Bit Server VM (build 21.0.2+13-Ubuntu-122.04.1, mixed mode, sharing)

And this has nothing to do with Kotlin except that also() is borrowed from Kotlin.

Folks, I know that this looks like a duplicate, but it isn’t. The other discussions say that the class with main() must be declared first (which is true), and that one should use javac then java, which is not exactly how she-bang scripts work. This is Q&A-style and I post my solution along with this question. If you know why it happens and how it may be avoided without having to desist from using some language constructs, please contribute.

2

Answers


  1. Chosen as BEST ANSWER

    I do not know how to fix it, but I know a workaround. The workaround is: remove any static imports for local classes. Java seems to search for main() in the class mentioned in the first static import. So in this case if we remove:

    import static gomad.MyKotlin.*;
    

    and replace

        return also(xs, Arrays::sort);
    

    with

        return MyKotlin.also(xs, Arrays::sort);
    

    and then everything works (but becomes cumbersome).


  2. There are more workarounds.

    The 2nd workaround is rather a bad news. If you add the main() method to the class where Java says it expects to see it, it will work. At least, until the moment that your code changes so that Java will expect to find main() somewhere else.

    This abomination works:

    #!/usr/bin/java --source 11
    
    package gomad;
    
    import java.util.Arrays;
    import java.util.function.Consumer;
    import java.util.stream.Collectors;
    
    import static gomad.MyArrays.sorted;
    import static gomad.MyKotlin.*;
    
    public class Main {
        public static void main(String[] args) {
            System.out.println("Hello world!");
            int[] x = sorted(3, 14, 15, 92, 6, 53, 58, 97, 93, 2, 38, 4, 62, 6, 43);
            System.out.println(Arrays.stream(x).boxed().collect(Collectors.toList()));
        }
    }
    
    class MyKotlin {
        static <T> T also(T obj, Consumer<T> action) {
            action.accept(obj);
            return obj;
        }
    }
    
    class MyArrays {
        static int[] sorted(int ... xs) {
            return also(xs, Arrays::sort);
        }
    
        public static void main(String[] args) { // FACEPALM
            Main.main(args);
        }
    }
    

    And the 3rd workaround is to create a class with a reference to statically imported main():

    #!/usr/bin/java --source 11
    
    package gomad;
    
    import java.util.Arrays;
    import java.util.function.Consumer;
    import java.util.stream.Collectors;
    
    import static gomad.Main.main;
    import static gomad.MyArrays.sorted;
    import static gomad.MyKotlin.*;
    
    class Irrelevant {
        void irrelevant() {
            main(); // not used, but compiled first
        }
    }
    
    public class Main {
        public static void main(String... args) {
            System.out.println("Hello world of " + Arrays.asList(args));
            int[] x = sorted(3, 14, 15, 92, 6, 53, 58, 97, 93, 2, 38, 4, 62, 6, 43);
            System.out.println(Arrays.stream(x).boxed().collect(Collectors.toList()));
        }
    }
    
    class MyKotlin {
        static <T> T also(T obj, Consumer<T> action) {
            action.accept(obj);
            return obj;
        }
    }
    
    class MyArrays {
        static int[] sorted(int ... xs) {
            return also(xs, Arrays::sort);
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search