Re: Java interop: "Can't call public method of non-public class"

462 views
Skip to first unread message

Shlomi Vaknin

unread,
Mar 12, 2013, 8:26:00 PM3/12/13
to clo...@googlegroups.com
hey, I have a similar problem, even when i type annotate with clojure 1.5 i still get that error.. any suggestions?

shlomi...@gmail.com

unread,
Mar 12, 2013, 8:46:12 PM3/12/13
to clo...@googlegroups.com
I meant to post this as a reply to https://groups.google.com/d/msg/clojure/jYfEKVH5GsQ/3Hq5hU0u6UQJ

In my case i am trying to get clojure working with netty 4, here is the code:

(def #^AbstractBootstrap b (ServerBootstrap.)) 
(.channel ^AbstractBootstrap b ^Class io.netty.channel.socket.nio.NioServerSocketChannel)  

which returns the error:
java.lang.IllegalArgumentException: Can't call public method of non-public class: public io.netty.bootstrap.AbstractBootstrap io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class)  
 at clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)  

why does it still use the reflector even though its fully annotated, and how do i get over that non-public class issue?..

Sean Corfield

unread,
Mar 12, 2013, 10:15:51 PM3/12/13
to clo...@googlegroups.com
On Tue, Mar 12, 2013 at 5:46 PM, <shlomi...@gmail.com> wrote:
> In my case i am trying to get clojure working with netty 4, here is the
> code:
>
> (def #^AbstractBootstrap b (ServerBootstrap.))
> (.channel ^AbstractBootstrap b ^Class
> io.netty.channel.socket.nio.NioServerSocketChannel)
>
> which returns the error:
> java.lang.IllegalArgumentException: Can't call public method of non-public
> class: public io.netty.bootstrap.AbstractBootstrap
> io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class)
> at clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)

I can't reproduce this (with Clojure 1.5.1, Netty 4.0.0.Alpha8):

user=> (set! *warn-on-reflection* true)
true
user=> (import '(io.netty.bootstrap AbstractBootstrap ServerBootstrap))
io.netty.bootstrap.ServerBootstrap
user=> (def b (ServerBootstrap.))
#'user/b
user=> (.channel ^AbstractBootstrap b ^Class
io.netty.channel.socket.nio.NioServerSocketChannel)
#<ServerBootstrap ServerBootstrap(factory: NioServerSocketChannel.class)>
user=> *clojure-version*
{:major 1, :minor 5, :incremental 1, :qualifier nil}
user=>


Can you provide more detail?
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

shlomi...@gmail.com

unread,
Mar 13, 2013, 5:16:25 AM3/13/13
to clo...@googlegroups.com
Hey Sean,

Your attempt worked because in Netty 4.0.0.Alpha8 AbstractBootstrap was still  public.. Try it out with 4.0.0.Beta2..

Thanks!

Marko Topolnik

unread,
Mar 13, 2013, 5:19:25 AM3/13/13
to clo...@googlegroups.com
When you annotate, is it a runtime or a compile-time error? I would be very surprised if it happened at runtime (when the function is actuall called).

shlomi...@gmail.com

unread,
Mar 13, 2013, 5:21:40 AM3/13/13
to clo...@googlegroups.com
yes you are right, it is a compile-time error.. 

shlomi...@gmail.com

unread,
Mar 13, 2013, 5:23:31 AM3/13/13
to clo...@googlegroups.com
here is the full exception when compiling:

Exception in thread "main" java.lang.IllegalArgumentException: Can't call public method of non-public class: public io.netty.bootstrap.AbstractBootstrap io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class), compiling:(netty.clj:31:1)
at clojure.lang.Compiler$InstanceMethodExpr.eval(Compiler.java:1453)
at clojure.lang.Compiler.compile1(Compiler.java:7153)
at clojure.lang.Compiler.compile(Compiler.java:7219)
at clojure.lang.RT.compile(RT.java:398)
at clojure.lang.RT.load(RT.java:438)
at clojure.lang.RT.load(RT.java:411)
at clojure.core$load$fn__5018.invoke(core.clj:5530)
at clojure.core$load.doInvoke(core.clj:5529)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5336)
at clojure.core$compile$fn__5023.invoke(core.clj:5541)
at clojure.core$compile.invoke(core.clj:5540)
at user$eval7.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6619)
at clojure.lang.Compiler.eval(Compiler.java:6609)
at clojure.lang.Compiler.eval(Compiler.java:6582)
at clojure.core$eval.invoke(core.clj:2852)
at clojure.main$eval_opt.invoke(main.clj:308)
at clojure.main$initialize.invoke(main.clj:327)
at clojure.main$null_opt.invoke(main.clj:362)
at clojure.main$main.doInvoke(main.clj:440)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Can't call public method of non-public class: public io.netty.bootstrap.AbstractBootstrap io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class)

Marko Topolnik

unread,
Mar 13, 2013, 5:34:33 AM3/13/13
to clo...@googlegroups.com
It is almost certain that the method you want to call is inherited from a public ancestor. Annotate the call with that ancestor and it will work.


On Wednesday, March 13, 2013 10:23:31 AM UTC+1, shlomi...@gmail.com wrote:
here is the full exception when compiling:

Exception in thread "main" java.lang.IllegalArgumentException: Can't call public method of non-public class: public io.netty.bootstrap.AbstractBootstrap io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class), compiling:(netty.clj:31:1)
at clojure.lang.Compiler$InstanceMethodExpr.eval(Compiler.java:1453)
at clojure.lang.Compiler.compile1(Compiler.java:7153)
at clojure.lang.Compiler.compile(Compiler.java:7219)

shlomi...@gmail.com

unread,
Mar 13, 2013, 5:43:35 AM3/13/13
to clo...@googlegroups.com
hey
we dont need to be almost certain, we can just look at the code : https://github.com/netty/netty/blob/master/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java and see that it is not a public ancestor. more then that, i annotated the code and it didnt work. here is a repl dump, same as Sean did:

; nREPL 0.1.6-preview
user> (set! *warn-on-reflection* true) 
true
user> (import '(io.netty.bootstrap AbstractBootstrap ServerBootstrap)) 
io.netty.bootstrap.ServerBootstrap
user> (def b (ServerBootstrap.)) 
#'user/b
user> (.channel ^AbstractBootstrap b ^Class 
io.netty.channel.socket.nio.NioServerSocketChannel) 
Reflection warning, NO_SOURCE_PATH:1:1 - call to channel can't be resolved.
IllegalArgumentException Can't call public method of non-public class: public io.netty.bootstrap.AbstractBootstrap io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class)  clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)
user>  *clojure-version* 
{:major 1, :minor 5, :incremental 1, :qualifier nil}

Marko Topolnik

unread,
Mar 13, 2013, 5:58:06 AM3/13/13
to clo...@googlegroups.com
It does have a public descendant, though. It is not acceptable for you to annotate with ServerBootstrap? It really is bad practice to annotate with non-public classes.

shlomi...@gmail.com

unread,
Mar 13, 2013, 6:03:50 AM3/13/13
to clo...@googlegroups.com
I fully agree with you, only it doesnt work.. ServerBootstrap does not override that specific method, which is what causing this pain, so i dont know what other options i have.

here is my attempt:

user> (.channel ^ServerBootstrap b ^Class io.netty.channel.socket.nio.NioServerSocketChannel)
Reflection warning, NO_SOURCE_PATH:1:1 - call to channel can't be resolved.
IllegalArgumentException Can't call public method of non-public class: public io.netty.bootstrap.AbstractBootstrap io.netty.bootstrap.AbstractBootstrap.channel(java.lang.Class)  clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)



Marko Topolnik

unread,
Mar 13, 2013, 6:09:44 AM3/13/13
to clo...@googlegroups.com
I see, that's very unfortunate then. It fails even when it is not actually making the reflective call. Your last recourse is writing your own code against the Java Reflection API, using setAccesible(true) if necessary.

shlomi...@gmail.com

unread,
Mar 13, 2013, 6:11:46 AM3/13/13
to clo...@googlegroups.com
hmmm, that what i was afraid of :)
ill take my chances harassing the netty people now before i go down that path..

Marko Topolnik

unread,
Mar 13, 2013, 6:17:45 AM3/13/13
to clo...@googlegroups.com
The netty people are not to blame; harass Rich instead :)

shlomi...@gmail.com

unread,
Mar 13, 2013, 6:23:17 AM3/13/13
to clo...@googlegroups.com
That is very true.

is here the right place? is there a a different group/forum for the language developers? should i file a bug on github?

Marko Topolnik

unread,
Mar 13, 2013, 6:27:55 AM3/13/13
to clo...@googlegroups.com
You should first make a minimal example that reproduces this. I've just failed to do so with the following:

abstract class AbstractParent {
    public void x() { System.out.println("x"); }
}

public class ConcreteChild extends AbstractParent {
}

user> (set! *warn-on-reflection* true)
true
user> (.x (test.ConcreteChild.))
nil
user> (.x ^test.AbstractParent (test.ConcreteChild.))
nil
user> (def cc (test.ConcreteChild.))
#'user/cc
user> (.x cc)
Reflection warning, NO_SOURCE_PATH:1:1 - reference to field x can't be resolved.
nil
user> 

What must I add to break it?

Marko Topolnik

unread,
Mar 13, 2013, 6:31:08 AM3/13/13
to clo...@googlegroups.com
I did it: the method must be final. Apparently without that the child is compiled with an overriding method that I didn't even define!

Shlomi Vaknin

unread,
Mar 13, 2013, 6:36:05 AM3/13/13
to clojure
are you getting the same error? i was getting 
clojure.lang.Compiler$CompilerException: java.lang.VerifyError: class Test overrides final method methodA.()I, compiling:(NO_SOURCE_PATH:2)

what is your code?


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/p2tBMT-BIYc/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Marko Topolnik

unread,
Mar 13, 2013, 6:38:26 AM3/13/13
to clo...@googlegroups.com
Detailed finding: it doesn't fail at compile time; it always fails at runtime.

Reason: at compile time there is a reflection warning, which means that the method wasn't found and a reflective call was emited by the compiler.

Marko Topolnik

unread,
Mar 13, 2013, 6:43:11 AM3/13/13
to clo...@googlegroups.com
You didn't recompile everything, that's the reason. I got the same error the first time, that's how I realized that the child had overridden the method. This is an interesting finding in itself: javac provides the override precisely to avoid pitfalls such as this one. If the parent was public, this wouldn't have happened.

Anyway, to make it really minimal, put all the java code into a single ConcreteChild.java.

shlomi...@gmail.com

unread,
Mar 13, 2013, 6:44:05 AM3/13/13
to clo...@googlegroups.com
very interesting.. are you getting the same error as i originally got?

Marko Topolnik

unread,
Mar 13, 2013, 6:48:43 AM3/13/13
to clo...@googlegroups.com
Yes, the error is there. I also created a lein new app project, which by default creates a gen-classed main namespace:

(ns call-test.core (:gen-class))
(set! *warn-on-reflection* true)
(defn -main [& args] (.x (test.ConcreteChild.)))

Now try lein do clean, uberjar. It will report a reflection warning while AOT-compiling. 

Then do java -jar target/call-test-0.1.0-SNAPSHOT-standalone.jar and see it fail.

This cleanly separates compile time from runtime.

shlomi...@gmail.com

unread,
Mar 13, 2013, 7:02:25 AM3/13/13
to clo...@googlegroups.com
right, i managed to reproduce it here, thanks!

so i am opening a bug and linking back here

shlomi...@gmail.com

unread,
Mar 13, 2013, 7:16:10 AM3/13/13
to clo...@googlegroups.com

Thanks!

Marko Topolnik

unread,
Mar 13, 2013, 7:42:51 AM3/13/13
to clo...@googlegroups.com
Hm. You shouldn't have made ConcreteChild a nested class, that is a distraction that may set people on a wrong trail. In Java you are allowed to have as many package-private classes as you wish; the constraint is only on one public class. If you had ConcreteChild.java with this in it:

abstract class AbstractParent {
    public final int x() { return 6; }
}

public class ConcreteChild extends AbstractParent {}

it would have compiled.

shlomi...@gmail.com

unread,
Mar 13, 2013, 7:46:06 AM3/13/13
to clo...@googlegroups.com
oh, right. thats a good point.

ill re-post that correction on the bug report.. thanks for the review!
Reply all
Reply to author
Forward
0 new messages