Why does Eclipse compile this, but javac doesn't? - java

We have some unit tests which compile and run fine in Eclipse 3.4, but when we try to compile them using javac, it fails. I've managed to cut the code down to something small and self-contained, so it has no external dependencies. The code itself won't make much sense because it's all out of context, but that doesn't matter - I just need to find out why javac doesn't like this:
public class Test {
public void test() {
matchOn(someMatcher().with(anotherMatcher()));
}
void matchOn(SubMatcher matcher) {}
SubMatcher someMatcher() {
return new SubMatcher();
}
Matcher anotherMatcher() {
return null;
}
}
interface Matcher <U, T> {}
class BaseMatcher implements Matcher {
public BaseMatcher with(Matcher<?,?> matcher) {
return this;
}
}
class SubMatcher extends BaseMatcher {
#Override
public SubMatcher with(Matcher matcher) {
return this;
}
}
I've tried with JDK 1.5.0_10 and 1.6.0_13, with the same result:
Test.java:6: matchOn(test.SubMatcher) in test.Test cannot be applied to (test.BaseMatcher)
matchOn(someMatcher().with(anotherMatcher()));
^
1 error
I think this is perfectly valid Java. The SubMatcher.with() method returns a more specific type than BaseMatcher.with(), but the compiler seems to think that the return type is BaseMatcher. However, it's possible that the Eclipse compiler is incorrectly allowing something it shouldn't be.
Any ideas?

in BaseMatcher you need to specify type parameters:
public SubMatcher with(Matcher<?, ?> matcher) {
in order to allow javac to match your with method
PS
imho is a bug of eclipse compiler

I made it build successfully by adding <?,?> to Matcher in SubMatcher.with:
class SubMatcher extends BaseMatcher {
#Override
public SubMatcher with(Matcher<?,?> matcher) {
return this;
}
}
Without this, the method signature is different from the base. I wonder whether there's a bug in the #Override checking that fails to notice this.

Check which jre or jdk you are compiling with on both Eclipse and terminal. Maybe it might be the version issue.

Works for me:
$ java -version
openjdk version "1.7.0-internal"
OpenJDK Runtime Environment (build 1.7.0-internal-****-2009_07_23_10_21-b00)
OpenJDK 64-Bit Server VM (build 16.0-b06, mixed mode)
$ javac -XDrawDiagnostics Test.java
$
I vaguely remember such a bugreport, but cannot give you a link to it right now.

Related

default method in interface runs with command prompt but not in eclipse

interface G {
default void print() {
System.out.println("G");
}
}
class M {
public void print() {
System.out.println("M");
}
}
class GImpl extends M implements G {}
public class Wierd {
public static void main(String[] args) {
G g=new GImpl();
g.print();
}
}
i was trying to use default method in interface ,but when compile with eclipse i get error on line 2 -says delete default, but i compile and run with command prompt it runs fine,what could be the reason for this?
Your eclipse still not using Java8 version please check it might be less than 8.
Use System.out.println(System.getProperty("java.runtime.version")); to check.
You may also need to change build path of your project and compiler level in eclipse.
AND More Important:
Installing Java™ 8 support .
It seems that you have installed Java 8 JDK but your eclipse still does not support Java 8. Eclipse Luna has support for Java 8. To change the compiler options Right Click your project>Properties>Java Compiler>Check on "Enable project specific settings">Then select Compiler Compliance level.

Cannot reproduce result of Type Erasure example

I am reading 'Java Generics and Collections' section 8.4. The author defines the following code while trying to explain Binary Compatibility:
interface Name extends Comparable {
public int compareTo(Object o);
}
class SimpleName implements Name {
private String base;
public SimpleName(String base) {
this.base = base;
}
public int compareTo(Object o) {
return base.compareTo(((SimpleName)o).base);
}
}
class ExtendedName extends SimpleName {
private String ext;
public ExtendedName(String base, String ext) {
super(base); this.ext = ext;
}
public int compareTo(Object o) {
int c = super.compareTo(o);
if (c == 0 && o instanceof ExtendedName)
return ext.compareTo(((ExtendedName)o).ext);
else
return c;
}
}
class Client {
public static void main(String[] args) {
Name m = new ExtendedName("a","b");
Name n = new ExtendedName("a","c");
assert m.compareTo(n) < 0;
}
}
and then talks about making the Name interface and SimpleName class generic and leaving the ExtendedName as is. As a result the new code is:
interface Name extends Comparable<Name> {
public int compareTo(Name o);
}
class SimpleName implements Name {
private String base;
public SimpleName(String base) {
this.base = base;
}
public int compareTo(Name o) {
return base.compareTo(((SimpleName)o).base);
}
}
// use legacy class file for ExtendedName
class Test {
public static void main(String[] args) {
Name m = new ExtendedName("a","b");
Name n = new ExtendedName("a","c");
assert m.compareTo(n) == 0; // answer is now different!
}
}
The author describes the result of such an action as following:
Say that we generify Name and SimpleName so that they define
compareTo(Name), but that we do not have the source for ExtendedName. Since it defines
only compareTo(Object), client code that calls compareTo(Name) rather than compareTo(Object) will invoke the method on SimpleName (where it is defined) rather than
ExtendedName (where it is not defined), so the base names will be compared but the
extensions ignored.
However when I make only Name and SimpleName generic I get a compile time error and not what the author describes above. The error is:
name clash: compareTo(Object) in NameHalfMovedToGenerics.ExtendedName and compareTo(T) in Comparable have the same erasure, yet neither overrides the other
And this is not the first time I am facing such an issue - earlier while trying to read Sun documentation on erasure I faced a similar issue where my code doesn't show the same result as described by the author.
Have I made a mistake in understanding what the author is trying to say?
Any help will be much appreciated.
Thanks in advance.
This is an example of a problem that can occur under separate compilation.
The main subtlety with separate compilation is that, when a caller class is compiled, certain information is copied from the callee into the caller's class file. If the caller is later run against a different version of the callee, the information copied from the old version of the callee might not match exactly the new version of the callee, and the results might be different. This is very hard to see by just looking at source code. This example shows how the behavior of a program can change in a surprising way when such a modification is made.
In the example, Name and SimpleName were modified and recompiled, but the old, compiled binary of ExtendedName is still used. That's really what it means by "the source code for ExtendedName is not available." When a program is compiled against the modified class hierarchy, it records different information than it would have if it were compiled against the old hierarchy.
Let me run through the steps I performed to reproduce this example.
In an empty directory, I created two subdirectories v1 and v2. In v1 I put the classes from the first example code block into separate files Name.java, SimpleName.java, and ExtendedName.java.
Note that I'm not using the v1 and v2 directories as packages. All these files are in the unnamed package. Also, I'm using separate files, since if they're all nested classes it's hard to recompile some of them separately, which is necessary for the example to work.
In addition I renamed the main program to Test1.java and modified it as follows:
class Test1 {
public static void main(String[] args) {
Name m = new ExtendedName("a","b");
Name n = new ExtendedName("a","c");
System.out.println(m.compareTo(n));
}
}
In v1 I compiled everything and ran Test1:
$ ls
ExtendedName.java Name.java SimpleName.java Test1.java
$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
$ javac *.java
$ java Test1
-1
Now, in v2 I placed the Name.java and SimpleName.java files, modified using generics as shown in the second example code block. I also copied in v1/Test1.java to v2/Test2.java and renamed the class accordingly, but otherwise the code is the same.
$ ls
Name.java SimpleName.java Test2.java
$ javac -cp ../v1 *.java
$ java -cp .:../v1 Test2
0
This shows that the result of m.compareTo(n) is different after Name and SimpleName were modified, while using the old ExtendedName binary. What happened?
We can see the difference by looking at the disassembled output from the Test1 class (compiled against the old classes) and the Test2 class (compiled against the new classes) to see what bytecode is generated for the m.compareTo(n) call. Still in v2:
$ javap -c -cp ../v1 Test1
...
29: invokeinterface #8, 2 // InterfaceMethod Name.compareTo:(Ljava/lang/Object;)I
...
$ javap -c Test2
...
29: invokeinterface #8, 2 // InterfaceMethod Name.compareTo:(LName;)I
...
When compiling Test1, the information copied into the Test1.class file is a call to compareTo(Object) because that's the method the Name interface has at this point. With the modified classes, compiling Test2 results in bytecode that calls compareTo(Name) since that's what the modified Name interface now has. When Test2 runs, it looks for the compareTo(Name) method and thus bypasses the compareTo(Object) method in the ExtendedName class, calling SimpleName.compareTo(Name) instead. That's why the behavior differs.
Note that the behavior of the old Test1 binary does not change:
$ java -cp .:../v1 Test1
-1
But if Test1.java were recompiled against the new class hierarchy, its behavior would change. That's essentially what Test2.java is, but with a different name so that we can easily see the difference between running an old binary and a recompiled version.

Binary compatibility issue - an example?

As far as I understand the source compatibility and how you can easily show an example that would break source compatibility (change name of the method, remove method etc.), I am having a bit of a problem seeing how binary compatibility can be broken in practice. Does anyone have a simple example of preservation of source compatibility that would cause binary compatibility issues i.e. no code changes are required but recompilation is necesssary?
One example (and this is by no means the only one) would be if the signature of a method in a library changes, in a compatible way. For example, consider:
// Library.java v1
public class Library {
public static void print(String foo) {
System.out.println(foo);
}
}
// Client.java v1
public class Client {
public static void main(String[] args) {
Library.print("hello");
}
}
Compile and run:
$ javac Client.java Library.java
$ java Client
hello
Now change Library.java - note the type of the foo parameter:
// Library.java v2
public class Library {
public static void print(Object foo) {
System.out.println(foo);
}
}
Just recompile Library.java and try to rerun Client:
$ javac Library.java
$ java Client
Exception in thread "main" java.lang.NoSuchMethodError: Library.print(Ljava/lang/String;)V
at Client.main(Client.java:3)
First need to understand both compatibility.
Source compatibility - Program is source compatible with new version if Program can be compiled with that new version of code(library or api)
Binary compatibility - Program is binary compatible with new version of code if Program can be linked with that code without recompilation
Following link has more example for "Source compatible but Binary incompatible "
Specialising Return Types
Generalising Parameter Types
Primitive vs Wrapper Types
Read http://praitheesh.blogspot.com.au/2014/09/compatibility-and-api-evolution-in-java.html for more details.
If you import an interface with string constants.
(An anti-pattern in Java.)
Then the importing class copies the constants in the constant table, and uses those constants immediately. The import dependency to the interface then is missing.
When the string value of the constant in the interface is changed, the compiler does not see that it needs to recompile the class that will remain using the old value - as there is no longer an import to the interface.
The running is not broken, but the behaviour is - wrong value.
An example I met :
public class Class1 {
public void do() {
System.out.println("do!");
}
}
Client part :
public class Class2 {
public void callDo() {
Class1 c = new Class1();
c.do();
}
}
Now you change the return of do method :
public class Class1 {
public String do() {
System.out.println("do!");
return "done!";
}
}
If you run the client code without a recompilation you will get a NoSuchMethodError exception because the method signature has changed.

Java compilation error: switch on enum

I came across a very weird error that I just can't figure out how to solve.
A project, that compiles just fine on Windows, doesn't compile on Linux with the following error:
Cannot switch on a value of type AClass.Bbb. Only convertible int values, strings or enum variables are permitted, even though the stated type is an enum.
The code of the class is something along these lines:
public class AClass {
private enum Bbb {
ONE,
TWO;
}
public void aMethod(List<Bbb> arg) {
for (Bbb en : arg) {
switch (en) {
....
}
}
}
}
The en in switch(en) is underlined, with the error notification stated above.
Has anyone else had it? Is there a way to solve this?
UPD Java version:
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
The problem should go away if u r using JDK1.7 .Try following the below steps and see
Open the project properties
Click on "Java Compiler"
Checkmark "Enable project specific settings"
Set all of the drop down menus to 1.7
Hit ok
Clean the project to trigger a rebuild
If u do below , then the problem reappears.
Open the project properties
Click on "Java Compiler"
Clear "Enable project specific settings"
Hit ok
Clean the project to trigger a rebuild (it will rebuild automatically)
Here's what I see if I do the following...
Open the project properties
Click on "Java Compiler"
Click on "Configure Workspace Settings"
I see that the "Compiler compliance level" = 1.7
I see that "Use default compliance settings" is checked.
Hope this helps!!
I have tried your code
public class AClass {
enum Bbb {
ONE,
TWO;
}
public void aMethod(List<Bbb> arg) {
for (Bbb en : arg) {
switch (en) {
case ONE: System.out.println("ONE");break;
case TWO: System.out.println("TWO");break;
}
}
}
public static void main(String[] args) {
List<Bbb> list = new ArrayList<Bbb>();
list.add(Bbb.ONE);
list.add(Bbb.TWO);
new AClass().aMethod(list);
}
}
It is working fine.I dont know the pros and cons of passing argument like this List<Bbb> arg but atleast it is not error as much as i know in java 7
I don't know whether it is windows/linux related issue. But
From jdk5 onwards you can use enum in switch case and from jdk7 you can use String in switch case. While using enum in switch make sure that:
you are using jdk5 and later
All the labels used in your switch must be a valid enum object inside
your enum being used in switch.
In java enum is implemented through class concept(Each n every enum in java extends to Enum class that is an abstract class and direct sub class of Object class). So while creating enum like
public enum Bbb {
ONE,
TWO;
}
It will internally meant
public static final Bbb ONE=new Bbb();
public static final Bbb TWO=new Bbb();
Means all your defined enum objects are public, final and static objects of defined enum class. If you are using something else as switch label, it will give a compile time error.
For each enum in java, super class Enum is final and all enum classes are internally implemented as final. So inheritance can not be used for enum in java. Means we are not allowed to use anything else in switch labels except our own class enum objects(Not even subclass objects, because enum class can't be inherited further)

Differing behaviour between Java 5 & 6 when overloading generic methods

I've run into an issue in Java's Generics in which the same code will compile and work fine in Java 6, but will fail to compile because of the same erasure in Java 5. I have a file TestErasure.java that has an overloaded method, called "method":
import java.util.ArrayList;
import java.util.List;
public class TestErasure {
public static Object method(List<Object> list) {
System.out.println("method(List<Object> list)");
return null;
}
public static String method(List<String> list) {
System.out.println("method(List<String> list)");
return null;
}
public static void main(String[] args) {
method(new ArrayList<Object>());
method(new ArrayList<String>());
}
}
In Java 5, I get the expected compilation error, stating that the erasure of "method" is the same:
$ javac -version
javac 1.5.0_19
$ javac TestErasure.java
TestErasure.java:10: name clash: method(java.util.List<java.lang.String>) and method(java.util.List<java.lang.Object>) have the same erasure
public static String method(List<String> list) {
^
TestErasure.java:17: method(java.util.List<java.lang.Object>) in TestErasure cannot be applied to (java.util.ArrayList<java.lang.String>)
method(new ArrayList<String>());
^
2 errors
However, Java 6 is able to compile and run this same code.
$ javac -version
javac 1.6.0_16
$ javac TestErasure.java
$ java TestErasure
method(List<Object> list)
method(List<String> list)
Based upon my current understanding of erasures (thanks to Jon Skeet and Angelika Langer), I actually expected the compilation error as thrown by Java 5 (unless something changed in how Java handled Generics--which I can not find on the Java 6 release notes). In fact, if I modify the return type of one of the overloaded methods:
public static Object method(List<Object> list) ...
public static Object method(List<String> list) ...
Java 6 also fails to compile because of the same erasures:
$ javac TestErasure.java TestErasure.java:5: name clash: method(java.util.List<java.lang.Object>) and method(java.util.List<java.lang.String>) have the same erasure
public static Object method(List<Object> list) {
^
TestErasure.java:10: name clash: method(java.util.List<java.lang.String>) and method(java.util.List<java.lang.Object>) have the same erasure
public static Object method(List<String> list) {
^
2 errors
It appears as if the return type in Java 6 somehow influences the selection of which overloaded method to use?
Can someone shed light on why the first example works in Java 6--it seems to go against the stated handling of overloaded generic methods?
More info:
Per David's suggestion, the original example, complied by javac 1.6, will run under the java 1.5:
$ javac -target 1.5 TestErasure.java
$ java -version
java version "1.5.0_19"
$ java TestErasure
method(List<Object> list)
method(List<String> list)
Found these bugs on Sun, which I think is what you're describing:
http://bugs.sun.com/view_bug.do?bug_id=6182950
http://bugs.sun.com/view_bug.do?bug_id=6730568

Categories

Resources