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.
Related
Here is the example code:
public class Whatever
{
public static void main(String[] args)
{
LazyManufacturer manufacturer = new LazyManufacturer();
Porsche oldPorsche = new Porsche();
Porsche newPorsche = new Porsche();
manufacturer.updateCar(oldPorsche, newPorsche);
// for a car that I do not know the brand of, I would instantiate Car directly. So Car cannot be abstract
}
}
public class Car {
private String carCommonAttribute;
public void updateAccordingTo(Car car) {
carCommonAttribute = car.carCommonAttribute;
System.out.println("common updated");
}
}
public class Porsche extends Car {
private String porscheExclusiveAttribute;
public void updateAccordingTo(Porsche car) {
super.updateAccordingTo(car);
porscheExclusiveAttribute = car.porscheExclusiveAttribute;
System.out.println("porsche exclusive updated");
}
}
public class Garbage extends Car {
private String garbageAttribute;
// similar to Porsche
}
public class LazyManufacturer {
public <T extends Car> void updateCar(T oldCar, T newCar) {
oldCar.updateAccordingTo(newCar);
}
}
I know this is a bad example, but it is good enough for illustrating what I'm trying to achieve.
Right now the output is "common updated". I would like to see "porsche exclusive updated" as well.
I understand that at compile time, startCar(car) would see start method in Car class as the best fit as its signature perfectly matches what it is looking for. However, is there a way to get around that at runtime? At runtime, startCar would find a better fit for the start method because Porsche is a narrower type, isn't it?
What mark is saying is Change your Car -> start() method to start() instead of start(Car). This way you will be able to achieve what you intent to do.
Usually classes in same hierarchy should have exactly same method signatures so that they override the base class method ( only when behaviour needs to change, of course child classes can also have more methods). There is no need to pass the Car instance to the method, as the object always has access to itself. The passed parameter serves no purpose. For the driver, yes driver needs to have a car as input. Why does the car needs itself as input to call a method?
"At runtime, startCar would find a better fit for the start method because Porsche is a narrower type, isn't it?"
Isn't.
At runtime, JVM has no spare time to choose methods's best fit depending on actual type of given argument.
Best fit is chosen at compile time, according to declared type of arguments.
As #markspace said, you probably want to override start(), instead of overloading it. To do so, make signatures of Car.start() and Porche.start() identical. Either remove the argument (it is not used anyway), or declare the argument as Car car in both cases.
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).
I seen this bit of code, Even though I think I got the concept of static in Java I am bit confused. Can any one explain it to me, how an object being static works?
My code:
package com.oracle.certification.sampleTest;
public class Person {
static class Mail {
static String desc="Male";
}
static Gender Mail=new Gender();
}
package com.oracle.certification.sampleTest;
public class Gender {
String desc="Gender";
}
package com.oracle.certification.sampleTest;
public class Human {
public static void main(String str[]) {
System.out.println(Person.Mail.desc);
}
}
When the class Human is run, the O/P is 'gender' not 'male', even though des= gender is nonstatic and des=male is static with static inner class. Also I don't need to import the classes in Hman? I am sorry that I have very little knowledge about inner classes, first of all.
Can any one explain it to me, How an object being static works?
Essentially, static in that context means that the entity in question is attached to the class itself, not to an object of the class. Hence, with static there is exactly one instance of what you declare:
class T {
public static int staticMember = 0; // one variable stored in memory
public int nonStaticMember = 0; // as many variables stored in memory as objects are created from the class
}
See also What does the 'static' keyword do in a class?
However, your question is not necessarily a misunderstanding of static classes, but a corner case of name resolution: You are declaring both a type and a member variable with the same name (Mail) in one scope (within the Person class) - while one might think that this should not even be possible, the Java language does allow this and defines a couple of rules to determine which one to use.
From the JLS:
A simple name may occur in contexts where it may potentially be interpreted as the name of a variable, a type, or a package. In these situations, the rules of §6.5 specify that a variable will be chosen in preference to a type.
...
static class Mail { // a Type
}
static Gender Mail ... // a Variable with the same name
// - the compiler chooses to use this one
...
Hence, when referencing Person.Mail, the compiler chooses the variable, not the type (the inner class).
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.
I have the following two classes:
public class Class1
{
public Class1 randomvariable; // Variable declared
public static void main(String[] args)
{
randomvariable = new Class1(); // Variable initialized
}
}
public class Class2
{
public static void ranMethod()
{
randomvariable.getSomething(); // I can't access the member "randomvariable" here even though it's public and it's in the same project?
}
}
I am very certain that it's a very fundamental thing I'm missing here, but what am I actually missing? The Class1 member "randomvariable" is public and so is the class and both classes are in the same project.
What do I have to do to fix this problem?
There are two problems:
Firstly, you're trying to assign a value to randomvariable from main, without there being an instance of Class1. This would be okay in an instance method, as randomvariable would be implicitly this.randomvariable - but this is a static method.
Secondly, you're trying to read the value from Class2.ranMethod, again without there being an instance of Class1 involved.
It's important that you understand what an instance variable is. It's a value associated with a particular instance of a class. So if you had a class called Person, you might have a variable called name. Now in Class2.ranMethod, you'd effectively be writing:
name.getSomething();
That makes no sense - firstly there's nothing associating this code with Person at all, and secondly it doesn't say which person is involved.
Likewise within the main method - there's no instance, so you haven't got the context.
Here's an alternative program which does work, so you can see the difference:
public class Person {
// In real code you should almost *never* have public variables
// like this. It would normally be private, and you'd expose
// a public getName() method. It might be final, too, with the value
// assigned in the constructor.
public String name;
public static void main(String[] args) {
Person x = new Person();
x.name = "Fred";
PersonPresenter.displayPerson(x);
}
}
class PersonPresenter {
// In a real system this would probably be an instance method
public static void displayPerson(Person person) {
System.out.println("I present to you: " + person.name);
}
}
As you can tell by the comments, this still isn't ideal code - but I wanted to stay fairly close to your original code.
However, this now works: main is trying to set the value of an instance variable for a particular instance, and likewise presentPerson is given a reference to an instance as a parameter, so it can find out the value of the name variable for that instance.
When you try to access randomvariable you have to specify where it lives. Since its a non-static class field, you need an instance of Class1 in order to have a randomvariable. For instance:
Class1 randomclass;
randomclass.randomvariable.getSomething();
If it were a static field instead, meaning that only one exists per class instead of one per instance, you could access it with the class name:
Class1.randomvariable.getSomething();