I have a small class which contains a static final variable and a static function:
public class GlobalConstants {
public static final boolean ONLINE = true;
public static boolean isOnline(){
return ONLINE;
}
}
(of course there is a bit of code duplication, but please ignore that)
I package my program in a runnable jar and get weird result:
log.debug(GlobalConstants.isOnline()); //prints false
log.debug(GlobalConstants.ONLINE); //prints true
I would expect that they both print "true".
Is the java compiler doing a optimization that causes this strange behavior, or am i missing something?
Have a look at this project I set up with your code. It works just as expected, your error must lie in some parts of the code you removed to simplify your code example.
And always consider that your static code must be executed. If you call the code above from a static context, some circular dependencies may result in some static variables not being initialized before being called
Cheers for the help guys! Found the problem, it appeared that my local maven repository had a lost copy of the library holding the GlobalConstants class. This class was included as dependency in the runnable jar. But that doesn't explain the "false, true" output, unless GlobalConstants.ONLINE is converted to a boolean value by a optimization by the java compiler (because it is a final). It still confuses me a bit.
[update]
I decompiled my class and indeed:
System.out.println(GlobalConstants.ONLINE);
System.out.println(GlobalConstants.isOnline());
is converted by java compiler in:
System.out.println(true);
System.out.println(GlobalConstants.isOnline());
Now it is all clear and this explains the result in my original post. Because even when it was using a wrong class version it should have produced the same result (true, true). But because java compiler does magic it is possible that it returned true, false.
Related
I'm sure you all know the behaviour I mean - code such as:
Thread thread = new Thread();
int activeCount = thread.activeCount();
provokes a compiler warning. Why isn't it an error?
EDIT:
To be clear: question has nothing to do with Threads. I realise Thread examples are often given when discussing this because of the potential to really mess things up with them. But really the problem is that such usage is always nonsense and you can't (competently) write such a call and mean it. Any example of this type of method call would be barmy. Here's another:
String hello = "hello";
String number123AsString = hello.valueOf(123);
Which makes it look as if each String instance comes with a "String valueOf(int i)" method.
Basically I believe the Java designers made a mistake when they designed the language, and it's too late to fix it due to the compatibility issues involved. Yes, it can lead to very misleading code. Yes, you should avoid it. Yes, you should make sure your IDE is configured to treat it as an error, IMO. Should you ever design a language yourself, bear it in mind as an example of the kind of thing to avoid :)
Just to respond to DJClayworth's point, here's what's allowed in C#:
public class Foo
{
public static void Bar()
{
}
}
public class Abc
{
public void Test()
{
// Static methods in the same class and base classes
// (and outer classes) are available, with no
// qualification
Def();
// Static methods in other classes are available via
// the class name
Foo.Bar();
Abc abc = new Abc();
// This would *not* be legal. It being legal has no benefit,
// and just allows misleading code
// abc.Def();
}
public static void Def()
{
}
}
Why do I think it's misleading? Because if I look at code someVariable.SomeMethod() I expect it to use the value of someVariable. If SomeMethod() is a static method, that expectation is invalid; the code is tricking me. How can that possibly be a good thing?
Bizarrely enough, Java won't let you use a potentially uninitialized variable to call a static method, despite the fact that the only information it's going to use is the declared type of the variable. It's an inconsistent and unhelpful mess. Why allow it?
EDIT: This edit is a response to Clayton's answer, which claims it allows inheritance for static methods. It doesn't. Static methods just aren't polymorphic. Here's a short but complete program to demonstrate that:
class Base
{
static void foo()
{
System.out.println("Base.foo()");
}
}
class Derived extends Base
{
static void foo()
{
System.out.println("Derived.foo()");
}
}
public class Test
{
public static void main(String[] args)
{
Base b = new Derived();
b.foo(); // Prints "Base.foo()"
b = null;
b.foo(); // Still prints "Base.foo()"
}
}
As you can see, the execution-time value of b is completely ignored.
Why should it be an error? The instance has access to all the static methods. The static methods can't change the state of the instance (trying to is a compile error).
The problem with the well-known example that you give is very specific to threads, not static method calls. It looks as though you're getting the activeCount() for the thread referred to by thread, but you're really getting the count for the calling thread. This is a logical error that you as a programmer are making. Issuing a warning is the appropriate thing for the compiler to do in this case. It's up to you to heed the warning and fix your code.
EDIT: I realize that the syntax of the language is what's allowing you to write misleading code, but remember that the compiler and its warnings are part of the language too. The language allows you to do something that the compiler considers dubious, but it gives you the warning to make sure you're aware that it could cause problems.
They cannot make it an error anymore, because of all the code that is already out there.
I am with you on that it should be an error.
Maybe there should be an option/profile for the compiler to upgrade some warnings to errors.
Update: When they introduced the assert keyword in 1.4, which has similar potential compatibility issues with old code, they made it available only if you explicitly set the source mode to "1.4". I suppose one could make a it an error in a new source mode "java 7". But I doubt they would do it, considering that all the hassle it would cause. As others have pointed out, it is not strictly necessary to prevent you from writing confusing code. And language changes to Java should be limited to the strictly necessary at this point.
Short answer - the language allows it, so its not an error.
The really important thing, from the compiler's perspective, is that it be able to resolve symbols. In the case of a static method, it needs to know what class to look in for it -- since it's not associated with any particular object. Java's designers obviously decided that since they could determine the class of an object, they could also resolve the class of any static method for that object from any instance of the object. They choose to allow this -- swayed, perhaps, by #TofuBeer's observation -- to give the programmer some convenience. Other language designers have made different choices. I probably would have fallen into the latter camp, but it's not that big of a deal to me. I probably would allow the usage that #TofuBeer mentions, but having allowed it my position on not allowing access from an instance variable is less tenable.
Likely for the same logical that makes this not an error:
public class X
{
public static void foo()
{
}
public void bar()
{
foo(); // no need to do X.foo();
}
}
It isn't an error because it's part of the spec, but you're obviously asking about the rationale, which we can all guess at.
My guess is that the source of this is actually to allow a method in a class to invoke a static method in the same class without the hassle. Since calling x() is legal (even without the self class name), calling this.x() should be legal as well, and therefore calling via any object was made legal as well.
This also helps encourage users to turn private functions into static if they don't change the state.
Besides, compilers generally try to avoid declaring errors when there is no way that this could lead to a direct error. Since a static method does not change the state or care about the invoking object, it does not cause an actual error (just confusion) to allow this. A warning suffices.
The purpose of the instance variable reference is only to supply the type which encloses the static. If you look at the byte code invoking a static via instance.staticMethod or EnclosingClass.staticMethod produces the same invoke static method bytecode. No reference to the instance appears.
The answer as too why it's in there, well it just is. As long as you use the class. and not via an instance you will help avoid confusion in the future.
Probably you can change it in your IDE (in Eclipse Preferences -> Java -> Compiler -> Errors/Warnings)
There's not option for it. In java (like many other lang.) you can have access to all static members of a class through its class name or instance object of that class. That would be up to you and your case and software solution which one you should use that gives you more readability.
It's pretty old topic but still up-to-date and surprisingly bringing higher impact nowadays. As Jon mentioned, it might be just a mistake Java's designers made at the very beginning. But I wouldn't imagine before it can have impact on security.
Many coders know Apache Velocity, flexible and powerful template engine. It's so powerful that it allows to feed template with a set of named objects - stricly considered as objects from programming language (Java originally). Those objects can be accessed from within template like in programming language so for example Java's String instance can be used with all its public fields, properties and methods
$input.isEmpty()
where input is a String, runs directly through JVM and returns true or false to Velocity parser's output). So far so good.
But in Java all objects inherit from Object so our end-users can also put this to the template
$input.getClass()
to get an instance of String Class.
And with this reference they can also call a static method forName(String) on this
$input.getClass().forName("java.io.FileDescriptor")
use any class name and use it to whatever web server's account can do (deface, steal DB content, inspect config files, ...)
This exploit is somehow (in specific context) described here: https://github.com/veracode-research/solr-injection#7-cve-2019-17558-rce-via-velocity-template-by-_s00py
It wouldn't be possible if calling static methods from reference to the instance of class was prohibited.
I'm not saying that a particular programming framework is better than the other one or so but I just want to put a comparison. There's a port of Apache Velocity for .NET. In C# it's not possible to call static methods just from instance's reference what makes exploit like this useless:
$input.GetType().GetType("System.IO.FileStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
I just consider this:
instanceVar.staticMethod();
to be shorthand for this:
instanceVar.getClass().staticMethod();
If you always had to do this:
SomeClass.staticMethod();
then you wouldn't be able to leverage inheritance for static methods.
That is, by calling the static method via the instance you don't need to know what concrete class the instance is at compile time, only that it implements staticMethod() somewhere along the inheritance chain.
EDIT: This answer is wrong. See comments for details.
In my code, I am trying to output the value of src in the expressions window.
public void doIt() {
String src = "test";
System.out.println(src);
}
In Eclipse. I set a breakpoint on a line 3, and I open the "Expressions" window.
I add an expression src to evaluate, and I get
I've used the Expressions features... COUNTLESS times in my years of Java debugging.. Why does this happen now?
I've just recently started using Eclipse Juno.. vs Indigo. Did they change the way Expressions work?
If your code use any generics, you may want to check this bug:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=341232
Occurs in all version of Eclipse up to 4.2. In short certain generics expressions cause Eclipse to completely fail on any evaluation (please see this example: https://bugs.eclipse.org/bugs/attachment.cgi?id=224760). Not sure whether your code uses any generics, but if so, this may be it. Note that it is enough to have one of the troublesome generics somewhere in your class, not necessary in your method.
I just spent TONS of time to figure out that if you will create a package "Foo" and inside this package you'll create class called "Foo", like this:
package Foo;
public class Foo{
public Foo () {};
}
After the point when you use this class first time in your program, you will not be able to use expressions anymore:
import Foo.Foo; //this is the devil i think
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
//debug expressions works fine
Foo tmp = new Foo();
//debug expressions wouldn't work anymore
}
});
}
This bug can be reprodused up to current Eclipse Neon 4.7.
Check if you have updated version of Eclipse, looks like this issue is fixed in Eclipse 3.3
My Eclipse Version is 3.8.2 and if I evaluate the expression on line 2 then I am also receiving the same error but at line 3 its evaluating properly.
I had the same problem and I remove a generic method in my code. It work for me.
(Assuming you're compiling 1.5+ code)I had the same problem, here's what I did to fix it:
Ensure that your java compiler version is 1.5+
Fix all syntax/class related errors pertaining to that class. I had a number of missing dependencies in my classpath.
make sure the JRE is also 1.5+ for that specific project
According to https://stackoverflow.com/a/21763943/733092, this happens when the class contains a generic method like
public <T extends something> T myMethod() {};
This is from Effective Java
Programs that use the int enum pattern are brittle. Because int enums are
compile-time constants, they are compiled into the clients that use them.
Can some one explain why the int enum pattern is called compiled type constant and what is meant by compiled into the clients?
Here s' an example of such a constant :
public static final int APPLE_FUJI = 0;
Suppose you have two files:
Foo.java:
public class Foo
{
public static final int SOMETHING = 1;
}
Bar.java:
public class Bar
{
public static void main(String[] args)
{
System.out.println(Foo.SOMETHING);
}
}
Compile them both, run java Bar and it will print out 1.
Now change Foo.java so that SOMETHING is 2, and recompile just Foo.java. Rerun java Bar and it will still print 1. The constant value will be copied to every piece of code that uses it, rather than asking for the value from Foo at execution time.
In practice, if you recompile everything any time anything changes, this isn't a problem.
The value '0' itself will be built into the .class files during compilation. If you then change that value, you have to recompile everything that uses it, including any client's code that uses your application/library.
If you don't, you won't get a warning, but rather incorrect behaviour.
If your compile-time constant is used solely in your code then it's less of a problem, assuming a complete clean/build cycle. If your code then reaches a wider audience then this becomes more of a problem.
I'm sure you all know the behaviour I mean - code such as:
Thread thread = new Thread();
int activeCount = thread.activeCount();
provokes a compiler warning. Why isn't it an error?
EDIT:
To be clear: question has nothing to do with Threads. I realise Thread examples are often given when discussing this because of the potential to really mess things up with them. But really the problem is that such usage is always nonsense and you can't (competently) write such a call and mean it. Any example of this type of method call would be barmy. Here's another:
String hello = "hello";
String number123AsString = hello.valueOf(123);
Which makes it look as if each String instance comes with a "String valueOf(int i)" method.
Basically I believe the Java designers made a mistake when they designed the language, and it's too late to fix it due to the compatibility issues involved. Yes, it can lead to very misleading code. Yes, you should avoid it. Yes, you should make sure your IDE is configured to treat it as an error, IMO. Should you ever design a language yourself, bear it in mind as an example of the kind of thing to avoid :)
Just to respond to DJClayworth's point, here's what's allowed in C#:
public class Foo
{
public static void Bar()
{
}
}
public class Abc
{
public void Test()
{
// Static methods in the same class and base classes
// (and outer classes) are available, with no
// qualification
Def();
// Static methods in other classes are available via
// the class name
Foo.Bar();
Abc abc = new Abc();
// This would *not* be legal. It being legal has no benefit,
// and just allows misleading code
// abc.Def();
}
public static void Def()
{
}
}
Why do I think it's misleading? Because if I look at code someVariable.SomeMethod() I expect it to use the value of someVariable. If SomeMethod() is a static method, that expectation is invalid; the code is tricking me. How can that possibly be a good thing?
Bizarrely enough, Java won't let you use a potentially uninitialized variable to call a static method, despite the fact that the only information it's going to use is the declared type of the variable. It's an inconsistent and unhelpful mess. Why allow it?
EDIT: This edit is a response to Clayton's answer, which claims it allows inheritance for static methods. It doesn't. Static methods just aren't polymorphic. Here's a short but complete program to demonstrate that:
class Base
{
static void foo()
{
System.out.println("Base.foo()");
}
}
class Derived extends Base
{
static void foo()
{
System.out.println("Derived.foo()");
}
}
public class Test
{
public static void main(String[] args)
{
Base b = new Derived();
b.foo(); // Prints "Base.foo()"
b = null;
b.foo(); // Still prints "Base.foo()"
}
}
As you can see, the execution-time value of b is completely ignored.
Why should it be an error? The instance has access to all the static methods. The static methods can't change the state of the instance (trying to is a compile error).
The problem with the well-known example that you give is very specific to threads, not static method calls. It looks as though you're getting the activeCount() for the thread referred to by thread, but you're really getting the count for the calling thread. This is a logical error that you as a programmer are making. Issuing a warning is the appropriate thing for the compiler to do in this case. It's up to you to heed the warning and fix your code.
EDIT: I realize that the syntax of the language is what's allowing you to write misleading code, but remember that the compiler and its warnings are part of the language too. The language allows you to do something that the compiler considers dubious, but it gives you the warning to make sure you're aware that it could cause problems.
They cannot make it an error anymore, because of all the code that is already out there.
I am with you on that it should be an error.
Maybe there should be an option/profile for the compiler to upgrade some warnings to errors.
Update: When they introduced the assert keyword in 1.4, which has similar potential compatibility issues with old code, they made it available only if you explicitly set the source mode to "1.4". I suppose one could make a it an error in a new source mode "java 7". But I doubt they would do it, considering that all the hassle it would cause. As others have pointed out, it is not strictly necessary to prevent you from writing confusing code. And language changes to Java should be limited to the strictly necessary at this point.
Short answer - the language allows it, so its not an error.
The really important thing, from the compiler's perspective, is that it be able to resolve symbols. In the case of a static method, it needs to know what class to look in for it -- since it's not associated with any particular object. Java's designers obviously decided that since they could determine the class of an object, they could also resolve the class of any static method for that object from any instance of the object. They choose to allow this -- swayed, perhaps, by #TofuBeer's observation -- to give the programmer some convenience. Other language designers have made different choices. I probably would have fallen into the latter camp, but it's not that big of a deal to me. I probably would allow the usage that #TofuBeer mentions, but having allowed it my position on not allowing access from an instance variable is less tenable.
Likely for the same logical that makes this not an error:
public class X
{
public static void foo()
{
}
public void bar()
{
foo(); // no need to do X.foo();
}
}
It isn't an error because it's part of the spec, but you're obviously asking about the rationale, which we can all guess at.
My guess is that the source of this is actually to allow a method in a class to invoke a static method in the same class without the hassle. Since calling x() is legal (even without the self class name), calling this.x() should be legal as well, and therefore calling via any object was made legal as well.
This also helps encourage users to turn private functions into static if they don't change the state.
Besides, compilers generally try to avoid declaring errors when there is no way that this could lead to a direct error. Since a static method does not change the state or care about the invoking object, it does not cause an actual error (just confusion) to allow this. A warning suffices.
The purpose of the instance variable reference is only to supply the type which encloses the static. If you look at the byte code invoking a static via instance.staticMethod or EnclosingClass.staticMethod produces the same invoke static method bytecode. No reference to the instance appears.
The answer as too why it's in there, well it just is. As long as you use the class. and not via an instance you will help avoid confusion in the future.
Probably you can change it in your IDE (in Eclipse Preferences -> Java -> Compiler -> Errors/Warnings)
There's not option for it. In java (like many other lang.) you can have access to all static members of a class through its class name or instance object of that class. That would be up to you and your case and software solution which one you should use that gives you more readability.
It's pretty old topic but still up-to-date and surprisingly bringing higher impact nowadays. As Jon mentioned, it might be just a mistake Java's designers made at the very beginning. But I wouldn't imagine before it can have impact on security.
Many coders know Apache Velocity, flexible and powerful template engine. It's so powerful that it allows to feed template with a set of named objects - stricly considered as objects from programming language (Java originally). Those objects can be accessed from within template like in programming language so for example Java's String instance can be used with all its public fields, properties and methods
$input.isEmpty()
where input is a String, runs directly through JVM and returns true or false to Velocity parser's output). So far so good.
But in Java all objects inherit from Object so our end-users can also put this to the template
$input.getClass()
to get an instance of String Class.
And with this reference they can also call a static method forName(String) on this
$input.getClass().forName("java.io.FileDescriptor")
use any class name and use it to whatever web server's account can do (deface, steal DB content, inspect config files, ...)
This exploit is somehow (in specific context) described here: https://github.com/veracode-research/solr-injection#7-cve-2019-17558-rce-via-velocity-template-by-_s00py
It wouldn't be possible if calling static methods from reference to the instance of class was prohibited.
I'm not saying that a particular programming framework is better than the other one or so but I just want to put a comparison. There's a port of Apache Velocity for .NET. In C# it's not possible to call static methods just from instance's reference what makes exploit like this useless:
$input.GetType().GetType("System.IO.FileStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
I just consider this:
instanceVar.staticMethod();
to be shorthand for this:
instanceVar.getClass().staticMethod();
If you always had to do this:
SomeClass.staticMethod();
then you wouldn't be able to leverage inheritance for static methods.
That is, by calling the static method via the instance you don't need to know what concrete class the instance is at compile time, only that it implements staticMethod() somewhere along the inheritance chain.
EDIT: This answer is wrong. See comments for details.
The eclipse compiler refuses to compile the following code, stating that the field s is not visible. (IBM's Aspect J compiler also refuses, stating that "s could not be resolved") Why is that?
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
The Java Language Specification states:
Otherwise, we say there is default
access, which is permitted only when
the access occurs from within the
package in which the type is declared.
The way I understand it, the field is declared and accessed in the same compilation unit, thus within the same package, and should therefore be accessible.
Even more strangely, adding a downcast from ? extends Test to Test makes the field visible, i.e. the following code compiles:
public class Test {
String s;
void foo(Object o) {
Test t = getClass().cast(o);
String os = t.s;
}
}
Have I stumbled across a compiler bug, or misunderstood the Java Spec?
Edit:
I am on another computer now. Here, javac accepts the code, but eclipse still doesn't. Versions on this machine:
Eclipse Platform
Version: 3.4.2 Build id:
M20090211-1700
JDK 1.6.0
Edit 2
Indeed, javac accepts the code. I had tested by running the ant build, which uses IBM's Ascpect J compiler ...
Try this:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[Edit to clarify]:
getClass().cast(o) returns an object of type 'capture#1-of? extends Test' and not Test. So the issue is related to generics and how the compiler treats it. I don't know the details of the spec on generics but given that some compilers (per comments here) do accept your code, then this is either a loop hole in the spec or some of these compilers are not entirely according to spec.
[Last thoughts]:
I believe the eclipse compiler is actually (carefully) correct here. The object o may in fact be an extension of Test (and defined in another package) and the compiler has no way of knowing if that is indeed the case or not. So it is treating it as the worst case of an instance of an extension defined in another package. It would have been super correct if adding a final qualifier to class Test would have allowed access to field s, but it does not.
Well, let's see. I'd say the compiler can't properly guarantee that foo() will be called by some entity within the package, and therefore can't guarantee that s is visible. For example, add
protected void bar() {
foo();
}
and then in some subclass Banana in another package
public void quux() { bar(); }
and oops! getClass() yields Banana, which cannot see s.
Edit: In a sense, other.package.Banana doesn't have a field s. If Banana were in the same package, it could still have its own s property, and would have to refer to Test's s via super.
I can't reproduce what you are saying. These both compile fine for me without warning, error or anything with javac directly.
WinXP, javac 1.6.0_16
No I tried with eclipse (v3.4.1, Build id: M20080911-1700) and for the first one it says:
The field Test.s is not visible
At least for Compiler Compliance level 1.6 and 1.5.
The funny thing being, if you look at the Quick-fix options it lists a Change to 's' resolution. Which of course doesn't solve the problem. So the eclipse compiler and the Quick-fix "generator" seem to have different views on this too ;-)
For Compiler Compliance level 1.4 (as was to be expected) in eclipse for the first one I get
s cannot be resolved or is not a field
and for the second one I get
Type mismatch: cannot convert from Object to Test
If I specify -source 1.4 and target -1.4 in the command line directly javac says for the first one
cannot find symbol
and for the second one I get
incompatible types
Actually in almost all cases, except when required by Generics, it's better (and safer) to use Java cast operator. I discussed it here. Java cast operator does look over verbose, but it's the right tool here.
Replacing cast method with the operator compiles just fine in Eclipse.
public class Test {
String s;
void foo(Object o) {
String os = ((Test) o).s;
}
}
I think that alphazero is correct here, that Eclipse is just over cautious.
Very weird. For an unknown reason (to me), the eclipse compiler requires an explicit cast:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
While the code perfectly compiles without the cast with Sun's JDK (I'm running version 1.6.0_16 on GNU/Linux).