It has been already clarified what's the difference between val and const val here.
But my question is, why we should use const keyword? There is no difference from the generated Java code perspective.
This Kotlin code:
class Application
private val testVal = "example"
private const val testConst = "another example"
Generates:
public final class ApplicationKt
{
private static final String testVal = "example";
private static final String testConst = "another example";
}
It's not always the same generated code.
If testVal and testConst were public, the generated code wouldn't be the same. testVal would be private with a public get, whereas testConst would be public, without any getter. So const avoids generating a getter.
In my opinion the main difference is that val means that no setter will be generated for the property (but a getter will be generated) and not that the value is constant, while a const val is a constant (like a Java's private/public static final xxx).
Example:
class Foo {
private val testVal: String
get() = Random().nextInt().toString()
}
As directly mentioned in the documentation, testConst can be used in annotation parameters, but testVal can't.
More generally speaking, const guarantees that you have a constant variable in the Java sense, and
Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1), reachability (§14.21), and definite assignment (§16.1.1).
You don't see the difference between generated code because your variables are private. Otherwise the result would have the getter for testVal:
public final class ApplicationKt {
#NotNull
private static final String testVal = "example";
#NotNull
public static final String testConst = "another example";
#NotNull
public static final String getTestVal() {
return testVal;
}
}
So in your particular case it is the same, except you can use const properties in annotations:
const val testVal: String = "This subsystem is deprecated"
#Deprecated(testVal) fun foo() { ... }
There are also differences in using them.
Example of constants(Kotlin):
class Constants {
companion object {
val EXAMPLE1 = "example1" // need companion and a getter
const val EXAMPLE2 = "example2" // no getter, but companion is generated and useless
#JvmField val EXAMPLE3 = "example3"; // public static final with no getters and companion
}
}
How to use(Java):
public class UseConstants {
public void method(){
String ex1 = Constants.Companion.getEXAMPLE1();
String ex2 = Constants.EXAMPLE2;
String ex3 = Constants.EXAMPLE3;
}
}
"Consts" are compile time Constants whereas "val" is used to define constants at run time.
This means, that "consts" can never be assigned to a function or any class constructor, but only to a String or primitive.
Example:
const val NAME = "M Sakamoto"
val PICon = getPI()
fun getPI(): Double {
return 3.14
}
fun main(args: Array<String>) {
println("Name : $NAME")
println("Value of PI : $PICon")
}
Output:
Name : M Sakamoto
Value of PI : 3.14
Related
I have a class :
data class Stam(#SerializedName("blabla") val blabla: String = "")
I want to do gson.fromJson("{\"blabla\":null}", Stam::class.java)
However, it will fail because blabla is not nullable.
I want to make it so if gson failed to deserialize some variable, it will take the default value I give it.
How to achieve that?
I don't think it is possible with GSON, this is one of the reasons why kotlinx.serialization library was created. With this library it is fairly easy:
#Serializable
data class Stam(#SerialName("blabla") val blabla: String = "") //actually, #SerialName may be omitted if it is equal to field name
Json { coerceInputValues = true }.decodeFromString<Stam>("{\"blabla\":null}")
I wouldn't say it is not possible in Gson, but Gson is definitely not the best choice:
Gson has no mention on Kotlin, its runtime and specifics, so one is better to use a more convenient and Kotlin-aware tool. Typical questions here are: how to detect a data class (if it really matters, can be easily done in Kotlin), how to detect non-null parameters and fields in runtime, etc.
Data classes in Kotlin seem to provide a default constructor resolvable by Gson therefore Gson can invoke it (despite it can instantiate classes instances without constructors using unsafe mechanics) delegating to the "full-featured" constructor with the default arguments. The trick here is removing null-valued properties from input JSON so Gson would keep "default-argumented" fields unaffected.
I do Java but I do believe the following code can be converted easily (if you believe Gson is still a right choice):
final class StripNullTypeAdapterFactory
implements TypeAdapterFactory {
// The rule to check whether this type adapter should be applied.
// Externalizing the rule makes it much more flexible.
private final Predicate<? super TypeToken<?>> isClassSupported;
private StripNullTypeAdapterFactory(final Predicate<? super TypeToken<?>> isClassSupported) {
this.isClassSupported = isClassSupported;
}
static TypeAdapterFactory create(final Predicate<? super TypeToken<?>> isClassSupported) {
return new StripNullTypeAdapterFactory(isClassSupported);
}
#Override
#Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !isClassSupported.test(typeToken) ) {
return null;
}
// If the type is supported by the rule, get the type "real" delegate
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new StripNullTypeAdapter<>(delegate);
}
private static final class StripNullTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegate;
private StripNullTypeAdapter(final TypeAdapter<T> delegate) {
this.delegate = delegate;
}
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegate.write(out, value);
}
#Override
public T read(final JsonReader in) {
// Another disadvantage in using Gson:
// the null-stripped object must be buffered into memory regardless how big it is.
// So it may generate really big memory footprints.
final JsonObject buffer = JsonParser.parseReader(in).getAsJsonObject();
// Strip null properties from the object
for ( final Iterator<Map.Entry<String, JsonElement>> i = buffer.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<String, JsonElement> property = i.next();
if ( property.getValue().isJsonNull() ) {
i.remove();
}
}
// Now there is no null values so Gson would only use properties appearing in the buffer
return delegate.fromJsonTree(buffer);
}
}
}
Test:
public final class StripNullTypeAdapterFactoryTest {
private static final Collection<Class<?>> supportedClasses = ImmutableSet.of(Stam.class);
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
// I don't know how easy detecting data classes and non-null parameters is
// but since the rule is externalized, let's just lookup it
// in the "known classes" registry
.registerTypeAdapterFactory(StripNullTypeAdapterFactory.create(typeToken -> supportedClasses.contains(typeToken.getRawType())))
.create();
#Test
public void test() {
final Stam stam = gson.fromJson("{\"blabla\":null}", Stam.class);
// The test is "green" since
Assertions.assertEquals("", stam.getBlabla());
}
}
I still think Gson is not the best choice here.
I am new in kotlin and not able to understand how the getter and setter behave in kotlin, so if I set the setter to private. Then what is the way of updating the value.
package foo
class Person() {
var name: String = "defaultValue"
private set
}
If you set your setter to be private, then this setter will be accessible only from within its class. In other words you can use normal assignment even when your setter is private but only from within the class.
class Person() {
var name: String = "defaultValue"
private set
fun foo(bar: String) {
name = bar // name can be set here
}
}
fun main(args: Array<String>) {
Person().name = "foo" // error. Name can be accessed but can not be modified here as its setter is private.
}
For more information check the Kotlin's Visibility documentation.
the kotlin code above will be transform to java code by kotlin compiler more like as below:
package foo;
public final class Person{
private String name = "defaultValue";
public final String getName(){
return name;
}
private final void setName(String name){
this.name=name;
}
}
which means you can only change the name field in the Person class. another situation is if you want to modify the name property with private setter out of the Person class. you can using java reflection instead, for example:
val person = Person();
val field = Person::name.javaField!!.apply { isAccessible = true }
field.set(person, "bob")
println(person.name)// "bob"
val setter = Person::class.java.getDeclaredMethod("setName", String::class.java)!!
.apply {
isAccessible = true
}
setter.invoke(person, "john")
println(person.name)// "john"
I've been battling the whole morning to lock down the serialVersionUID in a Kotlin class. I have a BaseModel which is extended by Project
abstract class BaseModel<T>(
var id: Int? = null,
private val fileName: String,
private val data: MutableList<T>,
private val indices: MutableMap<Int, T>
) : Serializable {
...
protected fun writeToDisk() {
val oos = ObjectOutputStream(BufferedOutputStream(FileOutputStream(fetchFileName())) )
oos.writeObject(fetchData());
oos.close();
}
}
And the project class:
class Project(
var name: String = "",
var repo: String = ""
) : BaseModel<Project>(
data = Data.projects,
indices = Data.projectsIndex,
fileName = "data/projects.dat"
), Serializable {
...
override fun toString(): String {
return "Project: id=${id}, name=${name}, repo=${repo}"
}
}
Every time I write to Disk and then change anything in the class and try to read it back again, I would get:
java.io.InvalidClassException: com.jvaas.bob.model.Project; local
class incompatible: stream classdesc serialVersionUID =
4156405178259085766, local class serialVersionUID =
2024101567466310467
I've tried adding:
private val serialVersionUID: Long = 1
to all classes with no effect.
Some examples on StackOverflow were using serialVersionUid which had no effect either (I believe this is intelliJ lowercasing the last two letters for some reason)
#JvmStatic doesn't work here since it's not an object, I've tried making it non-private with no success.
You can define serialVersionUID as a constant in a companion object:
abstract class BaseModel<T> : Serializable {
companion object {
private const val serialVersionUID: Long = -1
}
}
Constants are compiled to fields, and fields of a companion are stored as static fields of the class that contains companion. Therefore you get what you need – a private static field serialVersionUID in your serializable class.
The solution was actually much simpler than I thought, use a companion object. This now serializes perfectly and if I add more fields, it still serializes to disk and deserializes unless I change the serialVersionUID
Base:
abstract class BaseModel<T>(
var id: Int? = null,
private val fileName: String,
private val data: MutableList<T>,
private val indices: MutableMap<Int, T>
) : Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 1
}
...
}
Project:
class Project(
var name: String = "",
var repo: String = ""
) : BaseModel<Project>(
data = Data.projects,
indices = Data.projectsIndex,
fileName = "data/projects.dat"
), Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 1
}
override fun toString(): String {
return "Project: id=${id}, name=${name}, repo=${repo}"
}
}
Install this plugin: GenerateSerialVersionUID,use plugin to auto generate default serial version uid,usage: Click here.
I'm trying to convert the following code to Kotlin AND still have one of the classes (Foo) used by Java. What is the proper way of making this conversion?
Original Java:
public class Foo {
public static final String C_ID = "ID";
public static final String C_NAME = "NAME";
public static final String[] VALUES = {"X", "Y", "Z"};
public static String[] getAll() {
return new String[] {C_ID, C_NAME};
}
}
public class Bar {
public void doStuff() {
String var1 = Foo.C_ID;
String[] array1 = Foo.VALUES;
String[] array2 = Foo.getAll();
}
}
Auto conversion fo Foo to Kotlin
object Foo {
val C_ID = "ID"
val C_NAME = "NAME"
val VALUES = arrayOf("X", "Y", "Z")
val all: Array<String>
get() = arrayOf(C_ID, C_NAME)
}
Problem:
Bar class can no longer access C_ID or VALUES (error: "private access")
if I put "const" in front of C_ID, it works... but I cannot do the same with VALUES ("const" can ONLY be used on primatives or String)
Is there a different way I should be doing this (so both Java code and Kotlin code can access everything in Foo)?
The current semantics come from Kotlin Beta Candidate:
#JvmField and objects
We have made the strategy for generating pure fields (as opposed to get/set pairs) more predictable: from now on only properties annotated as #JvmField, lateinit or const are exposed as fields to Java clients. Older versions used heuristics and created static fields in objects unconditionally, which is against our initial design goal of having binary-compatibility-friendly APIs by default.
Also, singleton instances are now accessible by the name INSTANCE (instead of INSTANCE$).
According to this and to the reference, there are three ways of working with properties of a Kotlin object from Java:
Use Foo.INSTANCE.
By default, properties of object won't be static fields for Java, but Java can access the properties through Foo object instance -- Foo.INSTANCE.
So the expression will be Foo.INSTANCE.getC_ID().
Mark a property with #JvmStatic annotation:
object Foo {
#JvmStatic val C_ID = "ID"
//...
}
This will generate static getter for C_ID instead of Foo instance getter which will be accessible as Foo.getC_ID().
Use #JvmField annotation on property declaration:
object Foo {
#JvmField val C_ID = "ID"
//...
}
This will make Kotlin compiler generate a static field for Java instead of property.
Then in Java you can access it as a static field: Foo.C_ID.
But it won't work on properties without backing fields like all in your example.
For primitives, as you stated, one can use const which will have the same effect as #JvmField in terms of visibility in Java.
By the way, when it comes to methods, the situation is the same, and there is #JvmStatic annotation for them.
In your foo class you can put those properties and the method inside a companion object:
class Foo {
companion object {
val C_ID:String = "ID"
val C_NAME:String = "NAME"
#JvmField val VALUES = arrayOf("X", "Y", "Z")
fun getAll():Array<String> {
return arrayOf(C_ID, C_NAME)
}
}
}
Then you can call Foo.getAll(), and Foo.C_ID, Foo.C_NAME and Foo.VALUES.
You should be able to access the values "the kotlin way":
object Foo {
val C_ID = "ID"
val C_NAME = "NAME"
val VALUES = arrayOf("X", "Y", "Z")
val all: Array<String>
get() = arrayOf(C_ID, C_NAME)
}
fun main(args: Array<String>) {
Foo.all.forEach { it->println(it) }
}
With as result:
ID
NAME
Process finished with exit code 0
it's better if you create new kotlin file just for constants.
create Constants.kt file and paste below code.
object Constants {
val C_ID = "ID"
val C_NAME = "NAME"
val VALUES = arrayOf("X", "Y", "Z")
val all: Array<String>
get() = arrayOf(C_ID, C_NAME)
}
in your main activity you can access the constants by the constant name the android studio will automatically import the constants. here is my mainActivity:
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.architecturecompintro.Constants.C_ID
import com.example.architecturecompintro.Constants.C_NAME
import com.example.architecturecompintro.Constants.VALUES
import com.example.architecturecompintro.Constants.all
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val TAG = "info"
Log.i(TAG, C_ID)
Log.i(TAG,C_NAME)
for(item in VALUES) {
Log.i(TAG,item)
}
val arrayItem = all
for(item in arrayItem) {
Log.i(TAG,item)
}
}
}
I was able to get log output successfully
Suppose I have two classes, a Baseand a Implwhich extends Base.
package mypackage
open class Base
class Impl : Base()
How would I create a private property for the concrete Impl-Type (for internal use), with a public getter typed as the Base-Type, to achieve polymorphism? My initial approach was like this:
class Test {
private val myType = Impl()
get():Base
}
However, the Kotlin compiler complains:
Error:(30, 11) Kotlin: Getter return type must be equal to the type of the property, i.e. 'mypackage.Impl'
Basically, this is what it would look like in plain Java:
public class Test {
private Impl myImpl = new Impl();
public Base getBase() {
return myImpl;
}
}
How could one achieve this? Am I missing something?
P.S. I am aware of Backing Fields and creating custom methods as a workaround for getter, I was just curious on how to approach this in an elegant, Kotlin style manner.
If the property is private, so will be the getter. In this case, it doesn't matter what type it will have. If you want to have a public property of base type, you'll need to declare it separately:
private val _myType = Impl()
public val myType : Base
get() = _myType
You would code this the same as you did in Java, using two different properties. Unless you are ok with Impl never being specialized in the class. So here are many options:
// if you don't need Impl typed as Impl then just hold it as base
class Test1 {
public val base: Base = Impl()
}
// have both with pointing one reference at the other
class Test2 {
private val _impl = Impl()
public val base: Base = _impl
}
// have both, second one is a getter (no real benefit over Test2)
class Test3 {
private val _impl = Impl()
public val base: Base
get() = _impl
}
// use a function to do basically a cast
class Test4 {
private val _impl = Impl()
public fun asBase(): Base = _impl
}
Or don't worry about this other property, any use of grabbing the Impl can hold it as type Base:
class Test5 {
public val impl: Impl = Impl()
}
// later
val thing: Base = Test5().impl
Maybe you are looking to build this in a way with a common interface to get the base implementation?
open class Base {}
// a common way to get the implementation from within a class
interface Based {
val base: Base
}
class ImplAbc : Base()
class ImplXyz : Base()
class TestAbc : Based {
override val base: Base = ImplAbc()
}
class TestXyz : Based {
private val _impl = ImplXyz()
override val base: Base = _impl
}