I've started to learn Kotlin. My current background is Java. I found out that class properties in Kotlin are quite different from class fields in Java, even though they look similar. In his question I would like to gather together all technical differences between those two. This is what I’ve already figured out:
Java field and hiding vs Kotli properties and overriding
(and actually this pushed me to write this post):
In Java, a field of a base class is hidden by a field with the same name in derived class, so which field is used depends on the type of a reference to the object which contains the field, not the type of the object itself (fields are not overridden like methods are, so they don't depend on runtime type of an object). For example this code:
class A {
public String name = "A";
public void printMessage() {
System.out.println("Field accessed in method declared inside class A invoked form an object of " + getClass() + " : " + name);
}
}
class B extends A{
public String name = "B";
}
public class Main {
public static void main(String... args){
B b = new B();
System.out.println("Field from instance of class B pointed by reference to B : " + b.name);
A a = b;
System.out.println("Field from instance of class B pointed by reference to A : "+a.name);
a.printMessage();
}
}
prints this :
Field from instance of class B pointed by reference to B : B
Field from instance of class B pointed by reference to A : A
Field accessed in method declared inside class A invoked form an object of class B : A
In contrast Kotlin properties are fields accessed by automatically generated getters and setters. Properties are overridden (not hidden), so property access is resolved at runtime, and the code with similar meaning as above written in Kotlin:
open class A {
open val name = "A"
fun printMessage() {
println("Field accessed in method declared inside class A invoked form an object of $javaClass : $name")
}
}
class B(override val name : String = "B") : A()
fun main(args : Array<String>) {
val b : B = B()
println("Field from instance of class B pointed by reference to B : " + b.name)
val a : A = b;
println("Field from instance of class B pointed by reference to A : " + a.name)
a.printMessage()
}
prints this :
Field from instance of class B pointed by reference to B : B
Field from instance of class B pointed by reference to A : B
Field accessed in method declared inside class A invoked form an object of class B : B
Access level
Java fields are package – private by default. Kotlin properties are public by default.
Default initialization
Java fields are initialized with reasonable default values (as describet here : https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html).
Every Kotlin class property has to be created in a way which will allow it to provide explicitly given value when it will be accessed.
One can achieve this by initiallizer, constructor, setter, lazy initialization:
class InitValues(val inCtor : String = "Given in constructor"){
var byInitializer = "In initializer"
var initializedWithNull : String? = null
val valueGivenByGetter
get() : String {
return "This value is given by getter"
}
val byLazyInit : String by lazy { "This is lazy init" }
}
but value which should be returned has to be given – no defaults will be provided.
Are there any other technical differences regarding class fields / properties which may surprise Java programmer writing code in Kotlin?
(I’m not talking about additional features like for example delegated properties but those things which at first glance are similar to what exists in Java and can be delusive)
I would like to elaborate more on the differences between a Java field and a Kotlin Property. Have a look at the following examples of Java field and Kotlin property.
Examples
Java field:
class Product {
public int discount = 20;
}
Kotlin property:
class Product {
var discount: Int = 20
}
Auto-generated Accessors
The two examples above are not equivalent. Because in Kotlin, getters and setters are auto-generated for the properties. The Kotlin property above is equivalent to the following Java code:
class Product {
private int discount = 20;
public int getDiscount() {
return discount;
}
public void setDiscount(int discount) {
this.discount = discount;
}
}
So the difference between the Java field and Kotlin Property is that the Kotlin property creates a field and its accessors. When the property is a val, it creates a getter only. When the property is a var, it creates both a getter and a setter. And the field becomes private by default as shown in the above code, the discount is private. But you can access the discount using its getters and setters.
Custom Accessors
What if we want to implement some logic or validation inside the getters and setters in Kotlin? For example, when someone is setting the discount on the product, we want to make sure it's never above 85%. In this case, we can define the custom accessors for the discount property like following:
class Product {
var discount: Int = 20
set(value) {
if (value >= 85) {
field = 85
} else {
field = value
}
}
}
The field is a reserved keyword in Kotlin which holds the value as a backing field. The above code result in:
product.discount = 70;
println(product.discount) // 70
product.discount = 90;
println(product.discount) // 85
We can do similar things with the getter using the get() method.
That's it! Hope that helps.
Related
Consider the int a variables in these classes:
class Foo {
public int a = 3;
public void addFive() { a += 5; System.out.print("f "); }
}
class Bar extends Foo {
public int a = 8;
public void addFive() { this.a += 5; System.out.print("b " ); }
}
public class test {
public static void main(String [] args){
Foo f = new Bar();
f.addFive();
System.out.println(f.a);
}
}
I understand that the method addFive() have been overridden in the child class, and in class test when the base class reference referring to child class is used to call the overridden method, the child class version of addFive is called.
But what about the public instance variable a? What happens when both base class and derived class have the same variable?
The output of the above program is
b 3
How does this happen?
There are actually two distinct public instance variables called a.
A Foo object has a Foo.a variable.
A Bar object has both Foo.a and Bar.a variables.
When you run this:
Foo f = new Bar();
f.addFive();
System.out.println(f.a);
the addFive method is updating the Bar.a variable, and then reading the Foo.a variable. To read the Bar.a variable, you would need to do this:
System.out.println(((Bar) f).a);
The technical term for what is happening here is "hiding". Refer to the JLS section 8.3, and section 8.3.3.2 for an example.
Note that hiding also applies to static methods with the same signature.
However instance methods with the same signature are "overridden" not "hidden", and you cannot access the version of a method that is overridden from the outside. (Within the class that overrides a method, the overridden method can be called using super. However, that's the only situation where this is allowed. The reason that accessing overridden methods is generally forbidden is that it would break data abstraction.)
The recommended way to avoid the confusion of (accidental) hiding is to declare your instance variables as private and access them via getter and setter methods. There are lots of other good reasons for using getters and setters too.
It should also be noted that: 1) Exposing public variables (like a) is generally a bad idea, because it leads to weak abstraction, unwanted coupling, and other problems. 2) Intentionally declaring a 2nd public a variable in the child class is a truly awful idea.
From JLS
8.3.3.2 Example: Hiding of Instance Variables This example is similar to
that in the previous section, but uses
instance variables rather than static
variables. The code:
class Point {
int x = 2;
}
class Test extends Point {
double x = 4.7;
void printBoth() {
System.out.println(x + " " + super.x);
}
public static void main(String[] args) {
Test sample = new Test();
sample.printBoth();
System.out.println(sample.x + " " +
((Point)sample).x);
}
}
produces the output:
4.7 2
4.7 2
because the declaration of x in class
Test hides the definition of x in
class Point, so class Test does not
inherit the field x from its
superclass Point. It must be noted,
however, that while the field x of
class Point is not inherited by class
Test, it is nevertheless implemented
by instances of class Test. In other
words, every instance of class Test
contains two fields, one of type int
and one of type double. Both fields
bear the name x, but within the
declaration of class Test, the simple
name x always refers to the field
declared within class Test. Code in
instance methods of class Test may
refer to the instance variable x of
class Point as super.x.
Code that uses a field access
expression to access field x will
access the field named x in the class
indicated by the type of reference
expression. Thus, the expression
sample.x accesses a double value, the
instance variable declared in class
Test, because the type of the variable
sample is Test, but the expression
((Point)sample).x accesses an int
value, the instance variable declared
in class Point, because of the cast to
type Point.
In inheritance, a Base class object can refer to an instance of Derived class.
So this is how Foo f = new Bar(); works okay.
Now when f.addFive(); statement gets invoked it actually calls the 'addFive() method of the Derived class instance using the reference variable of the Base class. So ultimately the method of 'Bar' class gets invoked. But as you see the addFive() method of 'Bar' class just prints 'b ' and not the value of 'a'.
The next statement i.e. System.out.println(f.a) is the one that actually prints the value of a which ultimately gets appended to the previous output and so you see the final output as 'b 3'. Here the value of a used is that of 'Foo' class.
Hope this trick execution & coding is clear and you understood how you got the output as 'b 3'.
Here F is of type Foo and f variable is holding Bar object but java runtime gets the f.a from the class Foo.This is because in Java variable names are resolved using the reference type and not the object which it is referring.
I have a scala trait with a public UUID that has a default value:
trait pet {
var uuid_ : UUID = UUID.randomUUID
}
now I am creating multiple classes, also in scala:
class dog extends pet {
var foo = 1
}
class cat extends pet {
}
class fish extends pet {
}
After that I created a method in Java (old project with both languages mixed).
Here the snipped with my problem. In the variable somePet is an instance of dog, cat or fish. But it is not clear what of them exactly:
// printing all variables in the console for human testing
Serializer.printAllFields(somePet);
// The somePet Variable must be a pet
if(!pet.class.isAssignableFrom(somePet.getClass()))
throw new Exception("Not a pet.");
// get the UUID of the pet
UUID uuid_;
try {
Field f = pet.class.getField("uuid_");
f.setAccessible(true);
uuid_ = (UUID) f.get(somePet);
}catch(Exception e){
// no uuid found
throw e;
}
But when I run the code I get the following error:
Exception in thread "main" java.lang.NoSuchFieldException: uuid_
And the stacktrace points on the line with Field f = pet.class.getField("uuid_");.
But what is wrong with the code?
An alternative I thought was replacing this exact line with:
Field f = ntObj.getClass().getField("uuid_");
But this also fails.
So where is the variable uuid_?
Because when I print out all variables in the console of the current somePet with a Serializer, I get something like
* cat.uuid_ = 34d7a781-472d-4d98-861e-7cff08045445;
or
* dog.foo = 1
* dog.uuid_ = 34d7a781-472d-4d98-861e-7cff08045445;
in the console.
So the variable uuid_ is there with a default value.
(I am using the serializer from this post)
So how do I get the uuid_ variable in my java snippet?
First of all, there is no such Serializer under the the package java.util, so you're using something else.
An trait is translated to a Java interface, which cannot have fields per se.
However you're trying to access the field via the Pet interface
pet.class.getField(...)
That won't work. You need to look for the field inside the concrete class.
Also, the field will be private by default, since the access is granted via a getter.
The getField method is able to retrieve every public field for the entire class hierarchy (which mean even superclasses), while getDeclaredField is able to retrieve protected and private fields, but only on the exact class you're calling it from. You need also a call to setAccessible(true), because
A value of true indicates that the reflected object should suppress
Java language access checking when it is used. A value of false indicates that the reflected object should enforce Java language access checks
The correct code would be (dog is the concrete instance)
final Field uuid = dog.getClass().getDeclaredField("uuid_");
uuid.setAccessible(true);
final Object o = uuid.get(dog);
Or using the automatic getter
final Method uuid = dog.getClass().getMethod("uuid_");
final Object o = uuid_.invoke(dog);
I'm wondering whether is possible to access to the information contained in a field of a class from another class that's been created inside of the first one. I put a snippit of Java code for showing I want to do.
public class A {
public String c = new String();
B b;
...
...
...
public void doSomething() {
b = new B();
}
}
public class B {
...
...
...
public void retrieveInformationFromA() {
// I need to retrieve the field "c" of A instance that's
// created the B instance
}
}
NOTE: Due restrictions of the current design of this code I can't to create a constructor in B that includes a parameter for field "c" of A class. Due to legacy code, I have to avoid changing the existing code as much as possible.
I appreciated any help!!
UPDATE: I've corrected the code, I forgot to add the public modifier to the field "c" in A.
Since you did not specify any visibility (public, private, protected) to field 'c' it is implied to be "package protected" i.e. all classes in the same package as class A can access that field directly.
So, if your classes A and B are in the same package, you can access field 'c' directly from class B.
If they are not in the same package, you can not access it normally, but need to use reflection (and this should be the absolute last resort): How do I read a private field in Java?
Edit: But you still have to pass a reference to the instance of class A to the class B. If you can not change class B you are completely out of luck.
Declare the string as static
public class A {
static String c = new String();
and then access it in B like
public void retrieveInformationFromA() {
String info = A.c;
// I need to retrieve the field "c" of A instance that's
// created the B instance
}
If c needs to be different or non static
public void retrieveInformationFromA() {
A obj = new A();
String info = obj.c;
// I need to retrieve the field "c" of A instance that's
// created the B instance
}
In my experience that is not possible.
The called instance b has no knowledge of the world surrounding it apart from what you hand down.
You may include a setter with class B to hand 'c' down if you do not want or cannot declare c as static.
Another method could be an intermediate "InfoBase" class, which contains fields one or more different classes will need.
You cant retrieve the field "c" of A because its private member of class A. To access field in this case use getter setters(for c in this case) or make it public ( a really bad approach). Happy Coding:)
You could do something like this
// In class A
public void doSomething() {
b = new B();
b.retrieveInformationFromA(this);
}
...
// In class B
public void retrieveInformationFromA(A a) {
String c = a.c; // This way you can get it
// I need to retrieve the field "c" of A instance that's
// created the B instance
}
Trying to understand upcasting in Java. Recently observed strange behavior.
Example:
public class A extends B {
public int i = 2;
public void printI() {
System.out.println("print i = " + this.i);
}
public static void main(String[] args) {
B a = new A(); // <- upcasting here
System.out.println("i = " + a.i);
a.printI();
}
}
class B {
public int i = 1;
public void printI() {}
}
//Output:
//i = 1
//print i = 2
Seems, that upcasted object has two separate "i" properties. One "i" accessible directly (a.i) and the other through methods of child class (a.printI()).
Looks like upcasted object gets properties from superclass and methods from child class.
How object can have two separate "i"s?!
Seems, that upcasted object has two separate "i" properties.
Firstly, it's worth being clear about terminology. There's no such thing as an "upcasted object" - and "i" is a field in each of A and B.
But yes, there are two separate fields here. It's not like one field "overrides" another or anything like that.
It's not clear what you were trying to achieve, but the declaration of i in A shadows the declaration of i in B. See section 6.4 of the Java Language Specification for more information.
Note that in almost all cases, fields should be private - at which point the hiding really doesn't matter, as you wouldn't try to refer to a variable which wasn't declared in the class you're coding in anyway.
That's how Java works. You have both fields "available", they just happen to have the same name. When you reference from the subclass, it is hiding the superclass' version, but it is still there.
How can I get the fields associated only with the current class instead of all of its parent classes as well?
public class BaseClass()
{
public int x = 0;
}
public class AnotherClass() extends BaseClass
{
public int y = -1;
public int z = -2;
public void doStuff()
{
for(Field f : this.getClass().getFields())
{
//Save each field to a file
}
}
}
I want to get only Y and Z, which belong to AnotherClass. But the above gives me X as well.
This is meant to replace having to type each value that I want to save. It's not being saved in any typical format. It must be saved like this so don't suggest saving the fields in a different way.
Filtering out each field's name would defeat the purpose of this as there are well over 200.
You can get only the fields declared in the class with getDeclaredFields. It will exclude inherited fields.
You can filter based upon the Field's getDeclaredClass():
public static List<Field> fieldsDeclaredDirectlyIn(Class<?> c) {
final List<Field> l = new ArrayList<Field>();
for (Field f : c.getFields())
if (f.getDeclaringClass().equals(c))
l.add(f);
return l;
}
This picks just y and z for your example.
There may be a cleaner way to do this with flags to some function, but an obvious answer (and what I've done in the past) is to find the difference between the this.getClass().getFields() and the super.getClass().getFields() arrays.
Using public Field[] getDeclaredFields()
Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private fields, but excludes inherited fields.
Field[] fields = AnotherClass.class.getDeclaredFields();
for(Field f : fields){
System.out.println(f.getName());
}