I'm puzzled by the possibility to call new on an instance, like
InnerClass sc = pc.new InnerClass();
I understand how to use it, but my question is about fully understanding this. Like:
Where is it described in the JAVA documentation?
Is this a recommended solution that should be used, or is there a better way?
Why doesn't a plain "new" work?
I saw it in a code example, and I have worked out enough to understand that I'm unable to use a plain "new" in a static context.
This is the full context as a runnable example:
class ParentClass{
ParentClass(){
}
public static void main(String[] args){
ParentClass pc = new ParentClass();
InnerClass sc = pc.new InnerClass();
}
class InnerClass {
InnerClass() {
System.out.println("I'm OK");
}
}
}
Disclaimer: The terms "parent class" and "sub class" you use are not correct in your example, so my example below will use the correct terms "outer class" and "inner class" (thanks to #eis for the hint).
Where is it described in the JAVA documentation?
See #eis' comment to my answer for a link.
Is this a recommended solution that should be used, or is there a better way?
It depends – on what you need it for.
If SubClass doesn't need any information of an instance of ParentClass, it could (and should) be either made static or extracted to not be an inner class at all anymore. In that case, you can just call new on it without having an instance of ParentClass.
Why doesn't a plain "new" work?
Because SubClass may refer to information of the surrounding instance, which requires you to specify that instance. It's not a sub class in the sense that it extends ParentClass, but instead its type becomes a member of the outer class.
Consider this (and see it in action here):
public class OuterClass {
private int field;
public OuterClass(int field) {
this.field = field;
}
class InnerClass {
public int getOuterClassField() {
// we can access the field from the surrounding type's instance!
return OuterClass.this.field;
}
}
public static void main(String[] args) throws Exception {
OuterClass parent = new OuterClass(42);
// prints '42'
System.out.println(parent.new InnerClass().getOuterClassField());
// cannot work as it makes no sense
// System.out.println(new InnerClass().getOuterClassField());
}
}
If you were able to simply do new InnerClass(), there's no way of knowing what getOuterClassField should return since it is connected to the instance of its surrounding type (rather than just the type itself).
Related
I have some code that I need to reuse in several Java apps. That code implements a GUI which in turn needs to access some static variables and methods from the calling class. Those variables and methods are always called the same in all of the apps. Is there a generic way to obtain a handle to the calling class in Java so the code for "someGUI" class can remain untouched and in fact come from the same source file for all the different apps?
Minimal working example:
import javax.swing.*;
class test {
static int variable = 123;
public static void main(String[] args) {
someGUI sg = new someGUI();
sg.setVisible(true);
}
}
class someGUI extends JFrame {
public someGUI() {
System.out.println(String.format("test.variable = %d", test.variable));
}
}
How can I "generify" the reference to "test" in test.variable to always just refer to the calling class? It's not the "super" class, at least using super.variable doesn't work.
Firstly I would advise against this approach since there are only brittle ways to implement it. You should parameterize SomeGUI with a parameter containing the values you need instead.
However, it is possible to do what you ask by examining the thread's stack trace and using reflection to access the static fields by name. For example like this:
class Test {
static int variable = 123;
public static void main(String[] args) throws Exception {
SomeGUI sg = new SomeGUI();
}
static class SomeGUI extends JFrame {
public SomeGUI() throws Exception {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// stackTrace[0] is getStackTrace(), stackTrace[1] is SomeGUI(),
// stackTrace[2] is the point where our object is constructed.
StackTraceElement callingStackTraceElement = stackTrace[2];
String className = callingStackTraceElement.getClassName();
Class<?> c = Class.forName(className);
Field declaredField = c.getDeclaredField("variable");
Object value = declaredField.get(null);
System.out.println(String.format("test.variable = %d", value));
}
}
}
This will print test.variable = 123.
Obviously this is sensitive to renaming of the variables. It is also sensitive to dynamic proxies.
Also, it should be noted that you need to do this in the constructor. If you try to do this kind of lookup in other methods you can not find out how the instance was created.
There is no inheritance between somGUI and test,
Actual inheritance is there between someGUI and JFrame.
If you use super(), JVM tries to find 'variable' in JFrame, that is not what you wanted.
Use static methods setters & getters to access the 'variable' instead of direct accessing them.
Say if I have a dropdown in a form and I have another nested class inside of this class .
Now what's the best way to access this dropdown from the nested class?
Unlike Java, a nested class isn't a special "inner class" so you'd need to pass a reference. Raymond Chen has an example describing the differences here : C# nested classes are like C++ nested classes, not Java inner classes.
Here is an example where the constructor of the nested class is passed the instance of the outer class for later reference.
// C#
class OuterClass
{
string s;
// ...
class InnerClass
{
OuterClass o_;
public InnerClass(OuterClass o) { o_ = o; }
public string GetOuterString() { return o_.s; }
}
void SomeFunction() {
InnerClass i = new InnerClass(this);
i.GetOuterString();
}
}
Note that the InnerClass can access the "s" of the OuterClass, I didn't modify Raymond's code (as I linked to above), so remember that the "string s;" is private because no other access permission was specified.
Nested types aren't like inner classes in Java - there's no inherent instance of the containing type. (They're more like static nested classes in Java.) They're effectively separate classes, with two distinctions:
If the containing type is generic, the nested type is effectively parameterised by the containing type, e.g. Outer<int>.Nested isn't the same as Outer<string>.Nested.
Nested types have access to private members in the containing type.
Unlike Java, in C# there is no implicit reference to an instance of the enclosing class.
You need to pass such a reference to the nested class. A typical way to do this is through the nested class's constructor.
public partial class Form1 : Form
{
private Nested m_Nested;
public Form1()
{
InitializeComponent();
m_Nested = new Nested(this);
m_Nested.Test();
}
private class Nested
{
private Form1 m_Parent;
protected Form1 Parent
{
get
{
return m_Parent;
}
}
public Nested(Form1 parent)
{
m_Parent = parent;
}
public void Test()
{
this.Parent.textBox1.Text = "Testing access to parent Form's control";
}
}
}
Static Members
Since no one has mentioned it so far: Depending on your situation, if the member variable can also be static, you could simply access it in following way.
class OuterClass
{
private static int memberVar;
class NestedClass
{
void SomeFunction() { OuterClass.memberVar = 42; }
}
}
Sidenote: I marked memberVar purposefully (and redundantly) as private to illustrate the given ability of the nested class to access private members of it's outer class.
Caution / Please consider
In some situations this might be the easiest way/workaround to get access, but ...
Static also means, that the variable will be shared across all instance objects, with all the downsides/consequences there are (thread-safety, etc.)
Static also means, that this will obviously not work if you have more than one instance of the parent's class and the variable should hold an individual value for each instance
So in most cases you might wanna go with a different approach ...
Passing a Reference
As most people have suggested (and because it is also the most correct answer), here an example of passing a reference to the outer class' instance.
class OuterClass
{
private int memberVar;
private NestedClass n;
OuterClass() { n = new NestedClass(this); }
class NestedClass
{
private OuterClass parent;
NestedClass(OuterClass p) { parent = p; }
SomeFunction() { parent.memberVar = 42; }
}
}
One other method, which is useful under certain circumstances, is to derive the nested class off of the outer class. Like so:
class Outer()
{
protected int outerVar;
class Nested() : Outer
{
//can access outerVar here, without the need for a
// reference variable (or the associated dot notation).
}
}
I have used this technique especially in the context of Structured Unit Tests. (This may not apply to the OP's particular question, but it can be helpful with nested classes in general, as in the case of this "duplicate" question: " Can i access outer class objects in inner class ")
You could pass the enclosing class as a parameter to the nested class constructor, like this:
private NestedClass _nestedClass;
public ParentClass()
{
_nestedClass = new NestedClass(this);
}
Nested classes are generally not recommended and should be private and/or internal. They are, in my opinion, useful sometimes though.
Correct me if I am wrong, you are trying to process the outer control from inner class hence you ran into this. A better way of doing this would be to handle affairs in a event driven fashion. Use an Observer pattern, Register a listener on the outer control (your nested/inner class will be the listener). Makes life simpler. I am afraid that this is not the answer you were expecting!
send the master class as an constructor parameter to the nested (inner) class.
there is a good answer above but I like to write sth.
c# nested class is by default private
private to containing class if your want to use it must be public
Whenever we use static, we need not create a reference variable of a class. We can directly access class with the help of <class_name>
But when we write the following code:
class Abc
{
static void show()
{
System.out.println("Hey I am static");
}
}
class Test
{
public static void main(String args[])
{
Abc.show(); //1
new Abc().show(); //2
}
}
How does both the lines 1 & 2 work. what is the significance of
new Abc().show();
Using an instance (although it works) is the wrong way of invoking a static method (or access static fields) because static denotes its a member of the class/type and not the instance.
The compiler would therefore replace the instance with the Class type (due to static binding). In other words, at runtime, ABC.show() gets executed instead of new ABC().show().
However, your source code would still look confusing. Hence, it's considered a bad practice and would even result in warnings from IDEs like Eclipse.
Since your ABC class did'nt override the default constructor.
It's equivalent to :
class Abc{
public Abc(){super();}
static void show(){
System.out.println("Hey I am static");
}
}
Hence by doing new Abc().show(); you're just creating a new Abc object of your class and call the static method of the ABC class throught this object (it will show a warning because this is not the proper way to call static method).`
You CAN use static methods from an instance, as in new Abc().show(), but it's potentially confusing and not recommended.
Stick to className.staticMethod() for static methods and classInstance.instanceMethod() otherwise.
It simple means that you are creating object to ABC and than trying to accessing this variable through object.
However, at the time of compilation,
new Abc().show();
is converted to Abc.show().
Static keyword suggests only one copy per class now you have created method static and you are accessing using Classname.methodname() that is appropriate way because when class is loaded into JVM its instance will be created so no need to exlicitly create new Object of the class. hope it make sense.
According to Java, static variable are accessible by Class name but they are also accessible by class object even though Java don't suggest it, and it gives the same answer.
I know there will be only one copy of the variable and its value will be same for all objects and other things. Why does Java suggest to use class name instead of class object?
Because it can be confusing! There is no dynamic dispatching on static members.
Take a look at this confusing code: (might be syntax errors; my Java is rusty)
public abstract class Singer {
public static void sing() {
System.out.println("Singing");
}
}
public class Soprano extends Singer {
public static void sing() {
System.out.println("Singing in the range of C4-A5");
}
}
public class MyDriver {
public static void main(String[] argv) {
Singer mySoprano1 = new Soprano();
Soprano mySoprano2 = new Soprano();
mySoprano1.sing();
mySoprano2.sing();
}
}
Looking at MyDriver it's confusing because it seems like the sing method is polymorphic so the output should be...
Singing in the range of C4-A5
Singing in the range of C4-A5
... because both soprano1 and soprano2 are instances of Soprano - not Singer.
But alas, the output is actually:
Singing
Singing in the range of C4-A5
Why? Because there is no dynamic dispatch on static members, so the declared type of mySoprano1 determines which sing method is invoked... and the declared type of soprano1 is Singer, not Soprano.
For more, check out Puzzle 48 "All I get is static" in the book Java Puzzlers.
It is more self-documenting if you write MyClass.staticVariable than myObject.staticVariable. It tells the person looking at the code that staticVariable is a property of MyClass, as opposed to myObject which is a particular instance of the class.
One point I can think is if you use Class Reference instead of Object, JVM does not need to create a new Object at all to access that static variable. This is a good programming practice for performance.
Is it possible to create an anonymous class in Java like this:
public static void main(String[] args) {
AnonymousClass a = new AnonymousClass() {
int whatever = 1;
};
System.out.println(a.whatever);
}
I thought that this would be working but it doesn't. Do I misunderstand something with anonymous classes or is there only a syntax error?
You can do it this way:
public static void main(String[] args) {
System.out.println(new Object() {
int whatever = 1;
}.whatever);
}
That is, you can only dereference the fields and method directly from the instantiation expression. [Edit: Per the comments, you can use it where the compiler infers the type for you - which happens to be the instantion expression, or as a return value from a generic method you pass it to.] You can't store it in a variable and use fields/methods there, so it's not as useful as anonymous classes in e.g. C#.
Edit: You can, as previously stated by others, declare a method-local class:
public static void main(String[] args) {
class Local {
int whatever = 1;
}
Local local = new Local();
System.out.println(local);
}
Slightly wordy, though, and like non-static inner classes and regular anonymous classes, it retains an implicit reference to the enclosing this (in non-static methods).
If it was possible, we would not call them anonymous anymore: your example defines a class with a name: Anonymous. You may define an inner class with a name like this:
public static void main(String[] args) {
class NotAnonymous {
public int whatever = 1;
}
NotAnonymous na = new NotAnonymous();
System.out.println(na.whatever);
}
For this to work, AnonymousClass needs to be an Interface or a Class:
private interface AnonymousClass {
}
public static void main(String[] args) {
AnonymousClass a = new AnonymousClass() {
int whatever = 1;
};
System.out.println(a.whatever); // this won't work
}
EDIT
Corrected, as correctly stated in the comment, whatever won't accessible / present.
You are referring original anonymous class instance, which has no field "whatever" - so you can not reference it this way.
You can create your class like this, sure. However, the a.whatever call will fail, because the object type is still AnonymousClass, and it does not define whatever as an attribute.
If you overwrite some method or attribute that is already defined in the AnonymousClass class or interface, the object will use your implementation from the anonymous class instead of the old one, but not if you introduce new methods or attributes.