I have tried using Int and Integer types in Kotlin.Although I don't see any difference.
Is there a difference between Int and Integer types in Kotlin?Or are they the same?
Int is a Kotlin Class derived from Number. See doc
[Int] Represents a 32-bit signed integer. On the JVM, non-nullable values of
this type are represented as values of the primitive type int.
Integer is a Java Class.
If you were to search the Kotlin spec for "Integer", there is no Kotlin Integer type.
If you use the expression is Integer in IntelliJ, the IDE warns...
This type shouldn't be used in Kotlin, use Int instead.
Integer will interoperate well with Kotlin Int, but they do have some subtle differences in behavior, for example:
val one: Integer = 1 // error: "The integer literal does not conform to the expected type Integer"
val two: Integer = Integer(2) // compiles
val three: Int = Int(3) // does not compile
val four: Int = 4 // compiles
In Java, there are times where you need to explicitly "box" an integer as an object. In Kotlin only Nullable integers (Int?) are boxed. Explicitly trying to box a non-nullable integer will give a compiler error:
val three: Int = Int(3) // error: "Cannot access '<init>': it is private in 'Int'
val four: Any = 4 // implicit boxing compiles (or is it really boxed?)
But Int and Integer (java.lang.Integer) will be treated the same most of the time.
when(four) {
is Int -> println("is Int")
is Integer -> println("is Integer")
else -> println("is other")
} //prints "is Int"
when(four) {
is Integer -> println("is Integer")
is Int -> println("is Int")
else -> println("is other")
} //prints "is Integer"
A Int is a primitive Type. This is equivalent to JVM int.
A nullable Int Int? is a boxed Type. This is equivalent to java.lang.Integer.
Just have a look at https://kotlinlang.org/docs/reference/basic-types.html#representation
On the Java platform, numbers are physically stored as JVM primitive
types, unless we need a nullable number reference (e.g. Int?) or
generics are involved. In the latter cases numbers are boxed.
That means, that Int is represented as primitive int. Only in cases when nullability or generics are involved, the backing Wrapper Type Integer must be used.
If you use Integer from the beginning, you always work with the wrapper type and never with the primitive int.
Related
In the below code:
val sum = listOf(1, 2, 3).sumOf { if (it % 2 == 0) 1 else 0 }
Kotlin gives the following error:
Kotlin: Overload resolution ambiguity:
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Int): Int defined in kotlin.collections
public inline fun <T> Iterable<TypeVariable(T)>.sumOf(selector: (TypeVariable(T)) -> Long): Long defined in kotlin.collections
Playground
If I explicitly use toInt(), the error is gone but I get a warning of redundant call
val sum = listOf(1, 2, 3).sumOf { if (it % 2 == 0) 1.toInt() else 0 }
Why doesn't Kotlin automatically use Int here?
The spec says the following about the types of integer literals:
A literal without the mark has a special integer literal type
dependent on the value of the literal:
If the value is greater than maximum kotlin.Long value, it is an illegal integer literal and should be a compile-time error;
Otherwise, if the value is greater than maximum kotlin.Int value, it has type kotlin.Long;
Otherwise, it has an integer literal type containing all the built-in integer types guaranteed to be able to represent this value.
So integer literals like "1" doesn't have a simple type like kotlin.Int or kotlin.Long. It has an "integer literal type".
Example: integer literal 0x01 has value 1 and therefore has type ILT(kotlin.Byte,kotlin.Short,kotlin.Int,kotlin.Long). Integer literal 70000 has value 70000, which is not representable using types kotlin.Byte and kotlin.Short and therefore has type ILT(kotlin.Int,kotlin.Long).
Here are the subtyping rules of these ILTs. Importantly for your question:
∀Ti∈{T1,…,TK}:ILT(T1,…,TK)<:Ti
This rule basically says that ILTs work like an intersection type. For example, ILT(kotlin.Int,kotlin.Long) is a subtype of kotlin.Int and also a subtype of kotlin.Long.
Now let's look at your lambda { if (it % 2 == 0) 1 else 0 }. It returns either the literal 0 or the literal 1. These both have the type:
ILT(kotlin.Byte,kotlin.Short,kotlin.Int,kotlin.Long)
which is a subtype of kotlin.Long and kotlin.Int. Therefore, your lambda can be converted to both a (T) -> Long and a (T) -> Int, in the same way that a (T) -> Dog can be converted to a (T) -> Animal.
When you use toInt(), then only the (T) -> Int overload matches the return type, since Int is not convertible to Long implicitly.
Apparently, if you do toInt() on the whole expression, there is no redundant toInt warning:
fun main() {
val sum = listOf(1, 2, 3).sumOf { (if (it % 2 == 0) 1 else 0).toInt() }
}
Also note that the compiler looks at the lambda return type only because sumOf is annotated with OverloadResolutionByLambdaReturnType. If not for this, you would still get an ambiguity error even if you use toInt(). See Using lambda return type to refine function applicability for more info.
The reason why it is able to choose the Int overload in simple cases like:
fun foo(x: Int) {}
fun foo(x: Long) {}
fun main() { foo(43) }
is because of the "Choosing the most specific candidate" step in overload resolution. In this step, it handles built in numeric types differently, and considers Int the "most specific". However, this step happens just before "Using lambda return type to refine function applicability", and thinks that (T) -> Int and (T) -> Long are equally specific.
val var1: Any = "Carmelo Anthony"
I'm under the impression ::class.simpleName returns the variable type of an object
when I do the following:
val var1Type = var1::class.simpleName
print(var1Type)
I get String and not Any
but when I do this
val var2: String = var1
I get a Type mismatch: inferred type is Any but String was expected
In Kotlin, the ::class operator exists in 2 forms:
TypeName::class - which returns a KClass object for the static type TypeName.
variableName::class - which returns a KClass object corresponding to the runtime type of variableName, and not variableName's static type. (Kotlin calls this the "bound type" in their documentation).
In your case, var1 has a runtime tytpe of String but a static type of Any.
So var1::class returns the KClass for String, not Any.
But Kotlin's type system, like most statically typed languages, does not allow for implicit narrowing conversion (i.e. given a variable var2 typed as String, you cannot assign-to var2 from another variable (var3) statically-typed as Any, because var3 could have a runtime type that's completely incompatible with String, e.g. an InputStream object.
...even if it's provable (by following the program by-hand) that the Any-typed value will always be a String.
Fortunately, however, Kotlin's type-checker is modern and its "Smart cast" feature follows the scope of type-narrowing when the is operator is used, which is neat (TypeScript has it too, I don't think any other language does though).
In situations where you can't use Smart-casts or can otherwise prove to yourself that a downcast is safe then use the as operator to perform an unsafe cast. Like so: var2: String = var1 as String.
(Somewhat confusingly, other languages use as as the operator for safe casts, argh).
In context:
fun main() {
val var1: Any = "Carmelo Anthony"
val var1Type = var1::class.simpleName
println("var1's type: " + var1Type) // <-- This will print the *runtime type* of `var1` (String), not its static type (which is `Any`, *not* `String`).
/*
val var2: String = var1 // <-- Fails beause `var1` is `Any`, and `Any` is "wider" than `String`, and narrowing conversions always considered unsafe in languages like Kotlin, Java, etc.
*/
val var2Unsafe: String = var1 as String; // <-- Doing this is unsafe because it will throw if `var1` is not a String.
val var2Safe : String? = var1 as? String; // <-- Doing this is safe because it `var2Safe` will be null if `var1` is not a String.
println(var2Unsafe)
println(var2Safe)
}
If you're familiar with other languages, then here's an incomplete table of equivalent operations and their syntax:
Kotlin
Java
JavaScript
C#
C++
Get static type
TypeName::class
TypeName.class
ConstructorName
typeof(TypeName)
typeid(TypeName)
Get runtime type
variableName::class
variableName.getClass()
typeof variableName (intrinsics) variableName.constructor (objects)
variableName.GetType()
typeid(variableName)
Get type from name (string)
Class.forName( typeName ).kotlin
Class.forName( typeName )
eval( typeName ) (never do this)
Statically-defined runtime type check
variableName is TypeName
variableName instanceof TypeName
typeof variableName === 'typeName' (intrinsics) or variableName instanceof ConstructorName (objects)
variableName is TypeName
Runtime dynamic type check
otherKClass.isInstance( variableName ) or otherKType.isSubtypeOf()
otherClass.isAssignableFrom( variableName.getClass() )
otherType.IsAssignableFrom( variableName.GetType() )
Unsafe narrowing (aka downcast)
val n: NarrowType = widerVar as NarrowType;
NarrowType n = (NarrowType)widerVar;
variableName as TypeName (TypeScript only)
NarrowType n = (NarrowType)widerVar;
Safe narrowing (downcast or null)
val n: NarrowType? = widerVar as? NarrowType;
NarrowType n? = widerVar as NarrowType;
dynamic_cast<NarrowType>( widerVar )
Conditional narrowing in scope
variableName is TypeName
func(x: unknown): x is TypeName guard functions (TypeScript only)
widerVar is TypeName n
I am new to kotlin, and I have been doing research on the syntax of the language. It is to my understanding that in kotlin you can cast data types using integrated functions like :
.toInt()
converting 3.14 to an integer :
3.14.toInt()
since it is known that the readline() function returns a string i am not sure why this syntax is correct:
fun main() {
println("please enter a int:")
val num1 = readLine()!!.toInt()
println("one more")
val num2 = readLine()!!.toInt()
println("sum : ${num1 + num2}")
}
and this syntax is incorrect
fun main() {
println("please enter a int:")
val num1 = readLine().toInt()
println("one more")
val num2 = readLine().toInt()
println("sum : ${num1 + num2}")
}
returns the error:
Error:(5, 26) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String
Just looking for a bit more of an explanation on casting and how the syntax differs when it comes to the readline() function and functions alike.
The method readLine() returns a String? - the question mark means it can either be null or a String. In Kotlin, you need to handle instances with nullable type with either ? or !! when you're invoking a method onto that instance.
The difference is that ? only proceeds when the instance is not null, and !! forces it to proceed. The latter may give you a NullPointerException.
For example:
val num1 = readLine()?.toInt()
// Here, num1 could either be a String or null
val num1 = readLine()!!.toInt()
// if it goes to this next line, num1 is not null. Otherwise throws NullPointerException
readLine() returns String? (nullable version of String?)
Function toInt() receives String (non-nullable type).
fun String.toInt(): Int // non-nullable
fun String?.toInt(): Int // nullable (call)
You must do some kind of a null check to be sure that toInt will called on a non-nullable object. The !! operator converts nullable String? type to non-nullable String.
How can I create an instance of this Subject with an Int:
class NonNullableBehaviourSubject<T : Any>(defaultValue: T) : Subject<T>() {
private val behaviourSubject = BehaviorSubject.createDefault<T>(defaultValue)
}
I then create an instance of NonNullableBehaviourSubject like this:
val emailValidationSubject = NonNullableBehaviourSubject(Int)
But I then get a compile error using this:
emailValidationSubject.onNext(error?.errorResId)
The compile error I get is:
Required: Companion.Int
Found: Int?
Also, Int must not be nullable. Yet, NonNullableBehaviourSubject does define a nullable parameter.
Your error object has nullable type, and you use safe call (?.) on it, so the whole expression is nullable.
To make the value non-nullable you can either assert value is not null (error!!.errorResId will crash if error is null, error?.errorResId!! will crash if the whole expression is null) or provide the fallback value for null case ((error ?: DEFAULT_ERROR).errorResId or error?.errorResId ?: DEFAULT_ERROR_RES).
What is the difference between
x as? String
and
x as String?
They both seem to produce a String? type. The Kotlin page doesn't answer it for me.
UPDATE:
To clarify, my question is:
What is the purpose of having an as? operator at all, since for any object x and for any type T, the expression x as? T can be (I think) rephrased as x as T? ?
The difference lies in when x is a different type:
val x: Int = 1
x as String? // Causes ClassCastException, cannot assign Int to String?
x as? String // Returns null, since x is not a String type.
as? is the safe type cast operator. This means if casting fails, it returns null instead of throwing an exception. The docs also state the returned type is a nullable type, even if you cast it as a non-null type. Which means:
fun <T> safeCast(t: T){
val res = t as? String //Type: String?
}
fun <T> unsafeCast(t: T){
val res = t as String? //Type: String?
}
fun test(){
safeCast(1234);//No exception, `res` is null
unsafeCast(null);//No exception, `res` is null
unsafeCast(1234);//throws a ClassCastException
}
The point of the safe cast operator is safe casting. In the above example, I used the original String example with integers as the type. unsafeCast on an Int of course throws an exception, because an Int is not a String. safeCast does not, but res ends up as null.
The main difference isn't the type, but how it handles the casting itself. variable as SomeClass? throws an exception on an incompatible type, where as variable as? SomeClass does not, and returns null instead.