Java record constructor invisible through reflection - java

I'm playing with Java 15's new records feature, and how it interacts with reflection. I've run into some strange behaviour, where I can sometimes access a record's constructor via reflection, and sometimes not. For example, given the following Java file:
Recording.java:
public class Recording {
public static void main(String[] args) {
System.out.println("Constructors: " + MainRecord.class.getConstructors().length);
System.out.println("Methods: " + MainRecord.class.getDeclaredMethods().length);
}
record MainRecord(int i, String s) {}
}
This behaves as follows:
❯ javac --enable-preview --release 15 Recording.java
Note: Recording.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
❯ java --enable-preview Recording
Constructors: 0
Methods: 5
In other words, the call to getConstructors() does not find any constructors (while the call to `getDeclaredMethods() does find methods). I don't understand why not, because the constructor does exist:
❯ javap Recording\$MainRecord
Compiled from "Recording.java"
final class Recording$MainRecord extends java.lang.Record {
Recording$MainRecord(int, java.lang.String);
public final java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int i();
public java.lang.String s();
}
(Putting the record in a separate Java file gives the same results.)
However, if I do the same from JShell:
❯ jshell --enable-preview
| Welcome to JShell -- Version 15
| For an introduction type: /help intro
jshell> record JShellRecord(int i, String s) {}
| created record JShellRecord
jshell> JShellRecord.class.getConstructors().length
$2 ==> 1
So, now it does find the constructor.
Here's the Java version I'm using:
❯ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)
Compiling and running the same program from Java 14 does work:
❯ java -version
openjdk version "14.0.2" 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.2+12)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14.0.2+12, mixed mode, sharing)
❯ javac --enable-preview --release 14 Recording.java
Note: Recording.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
❯ java --enable-preview Recording
Constructors: 1
Methods: 5
I know that in Java 15, compared to Java 14, a number of restrictions have been put in place regarding reflection on records, but if I read the JEP correctly, those only apply to modification. Finding (and perhaps invoking) a constructor does not seem to apply.
Can anyone tell my what's going on here? What do I need to do to see a record's constructor in Java 15 through reflection?

getConstructors() returns public constructors only. Use getDeclaredConstructors() to get all constructors.
Your declaration record MainRecord(int i, String s) {} lacks the public modifier, so it creates a non-public class and also a non-public constructor. See JLS15-preview, §8.10.4
The implicitly declared canonical constructor has the same access modifier as the record class R, unless the record class lacks an access modifier, in which case the canonical constructor has package access
This does indeed differ from the JDK 14 preview. The beginning of the JDK 15 preview document says:
The changes are the same as those in the first preview of Records in Java SE 14, except for the following:
…
8.10.4 Removed requirement that canonical constructor must be public. Any access modifier must provide at least as much access as the record class. If a canonical constructor is implicitly declared, then its access modifier is the same as the record class.
It seems, top level classes created in JShell are implicitly public.
> jdk-15\bin\jshell --enable-preview
| Welcome to JShell -- Version 15
| For an introduction type: /help intro
jshell> record JShellRecord(int i, String s) {}
| created record JShellRecord
jshell> JShellRecord.class.getConstructors()[0]
$2 ==> public JShellRecord(int,java.lang.String)
jshell> java.lang.reflect.Modifier.isPublic(JShellRecord.class.getModifiers())
$3 ==> true
jshell>

Related

Java Nashorn compatibility file crashes

When I try to load the Nashorn compatibility file for Rhino (load("nashorn:mozilla_compat.js")) it comes up with the following error:
java.lang.RuntimeException: javax.script.ScriptException: ReferenceError: "net" is not defined in nashorn:mozilla_compat.js at line number 67
I've tried everything to get it to work but nothing has helped :(
This can happen if your script (not mozilla_compat.js itself) contains a declaration with a qualified name like this:
var x = new net.yourdomain.yourpackage.ClassName();
instead of doing
importPackage(Packages.net.yourdomain.yourpackage);
var x = new ClassName();
The former works in Rhino, but not in Nashorn, even with the compatibility script. The latter however will work in both environments.
I ran the following code with the latest JDK 8 update released (8u60) - available for download # http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
import javax.script.*;
public class Main {
public static void main(String[] ar) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
e.eval("load('nashorn:mozilla_compat.js')");
// this should print 'function' and mozilla_compat.js defines that function
e.eval("print(typeof importClass)");
}
}
And it printed "function" as expected. I checked it on jdk9-dev tip forest build as well. It works with that version as well. Will you please print "java -version" and make sure you're using recent JDK 8 ?

Java backward compatibility explanation

I have a java class file. I compiled with JVM 7. which I implemented java 1.7 additions like String switchcase, diamond operator. Now I want to run this .class file on java 1.6 JRE. Will it run?
A simple program using string switchcase As I uninstalled 6. Please try it out and give me answer
import java.util.Scanner;
public class Classing
{
public static void main(String[] args)
{
System.out.println("Enter a month to know who you are");
System.out.println("Jan \n Feb \n Mar \n Apr");
Scanner scan=new Scanner(System.in);
String name=scan.nextLine();
System.out.println(fortune(name.toLowerCase()));
}
public static String fortune(String s)
{
switch(s)
{
case "jan":
return "Good guy";
case "feb":
return "Nice guy";
case "mar":
return "Brave guy";
case "apr":
return "Super guy";
}
return " Month out of option"+s;
}
}
Java 7's switch on Strings compiles down to the same bytecodes which the Java 6 (and earlier) JRE executes. The same is true of the diamond operator. These are compiler features, not runtime features.
So while I've never tried, I would expect that if you compile code which uses these features using the Java 7 compiler, it should still run on the Java 6 JRE. Of course, if you try to compile that code using the Java 6 compiler, all you will get is a syntax error.
If you really want to know try it and see!
Even though this question has an accepted answer, according to http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#binary
The class file version for Java SE 7 is 51, as per the JVM Specification, because of the invokedynamic byte code introduced by JSR 292. Version 51 class files produced by the Java SE 7 compiler cannot be used in Java SE 6.
No it will not. Just because of the simple reason that ,JRE7 features are not available on JRE6.
It wont even compile if you just even copy paste the code which you implemented on 1.7 to new class of 1.6 ,because you used features which was introduced in 1.7 only

Check java version during program load

I'm looking for a way to check which java version my software is running under.
I'd like to verify during load time my software is running on at least
To get the java version you can use any of these depending on the version you want:
java.specification.version
java.version
java.vm.version
java.runtime.version
However, note that java versions are not equivalent between operative systems. So Java 6 on OSX does not mean the same thing as Java 6 on Windows. So, I would recommend you to also get the OS where the application is running, if you wish to determine if a given feature is available:
System.getProperty("os.name")
As a general guideline, all of this stuff is in the System package. A trick I use is iterate through all the available fields to have an idea of what I can use:
import java.util.Map;
class ShowProperties {
public static void main(String[] args) {
for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) {
System.out.println(e);
}
}
}
Use java.version property to retrieve the jre version.
String javaVersion = System.getProperty("java.version");
if ((!javaVersion.startsWith("1.6")) && (!javaVersion.startsWith("1.7")) && (!javaVersion.startsWith("1.8")) && (!javaVersion.startsWith("1.9")))
{
// error
}
You can use System.getProperty:
System.out.println(System.getProperty("java.version"));
1.7.0_21
java.lang.System.getProperty("java.version")

Using Windows device name such as Con as Java class name

I am referring to my previous question but this time I used the java compiler and the compiler compiles the output- it gives a weird output. And this time I used this instead of super.
This is the code of the program.
class Con {
int x = 10;
Con() {
this(2);
System.out.println("x :" + x);
}
Con(int i) {
x = i;
System.out.println("x :" + x);
}
}
class DemoCon {
public static void main(String args[]) {
Con c1 = new Con();
}
}
What do you think is the problem here? Is this a bug in Java?
Java version - 1.6.0 JDK
I used Eclipse to run the program and there is a Class not found exception.
A.java is the file name... We did a minor edit and made a public class called A.java too but the results are same. We further found out that the problem lies in the compiler.
On Windows it seems CON is reserved name and cannot be used for folders/directories or filenames.
The following
print "test" > Con.java
is not working.
Therefor the compiler is unable to create your Con.class and crashes.
From MSDN:
Do not use the following reserved device names for the name of a file:
CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. Also avoid these names followed immediately by an extension; for example, NUL.txt is not recommended
perhaps the problem exists because CON is a reserved file name (it was on MS-DOS -- see http://support.microsoft.com/kb/31157 http://www.computerhope.com/copyhlp.htm)
How did you compile it? On Win7 32b with Java 1.6 I get:
Type name is not valid. 'Con' is an invalid name on this platform.
Yes it looks like a bug. It compiled very well on my
Java(TM) SE Runtime Environment (build 1.6.0_26-b03-383-11A511)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-383, mixed mode)
environment on Mac OS X.
Maybe we can help further if you tell us whether you are using Sun (Oracle) JDK or OpenJDK?
Problem might be about class name (Con) and file name (A) are different (they should be the same) and you have two classes at the same level in a single file. Anyway, it compiles well on my box.
In java, the class name and file name must be exactly the same. In your case, your class name is Con, therefore, your class file must be Con.java. Since DemoCon is your class with the static void main(String[] args), your java file must be DemoCon.java.
I saved your code as DemoCon.java .
and ran it as
javac DemoCon.java
java DemoCon
o/p was
x :2
x :2

Accessor method visible under Windows, Linux, but not OS X

Trying to build against javax.vecmath using the 1.5.2 jar file (found on Java.net http://java3d.java.net/binary-builds.html, for example).
Try to make a call on, say Point3d;
public class Foo {
public static void main(String[] args) {
Point3d t = new Point3d(1.0, 1.0, 1.0);
System.out.println(t.getX());
}
}
In 64 bit Windows and Linux (I've only tried Ubuntu 10.04, 64 bit), this compiles and runs.
In OS X (10.6.7) it will not compile:
...: cannot find symbol
symbol : method getX()
location: class javax.vecmath.Point3d
System.out.println (t.getX());
This is using the exact same physical vecmath.jar
If instead I use the source directly, it compiles on OS X, but does not run
Exception in thread "main" java.lang.NoSuchMethodError: javax.vecmath.Point3d.getX()D
If I compile the sources myself on OS X to a jar file, and then use the jar in the example above, again, unable to compile.
Now, the fields being accessed are in javax.vecmath.Tuple3d, which is an abstract class with public fields for x, y and z. So on OS X this will work (actually, it seems to work everywhere).
public class Foo {
public static void main(String[] args) {
Point3d t = new Point3d(1.0, 1.0, 1.0);
System.out.println(t.x);
}
}
The thing is, I'm working on a code base that depends on vecmath.jar, and in which the maintainers are on Windows and wish to keep using the accessor methods, but I'm on OS X.
I'm looking to both:
(1) understand what is going on
(2) figure out how to make these sources portable while depending on the vecmath.jar file.
The "Exception in thread "main" java.lang.NoSuchMethodError: javax.vecmath.Point3d.getX()" indicates that not the 1.5.2 version but the Apple version 1.3 of vecmath.jar is accessed. The *getter" and "setter" methods were introduced in 1.5.
Check if Apple's out-dated Java 3D version 1.3 is installed in System/Library/Java/Extensions/ on your Mac. Remove all Java 3D 1.3 related files including vecmath.jar (jar, jnilib), they are useless.
August, InteractiveMesh

Categories

Resources