Нет, ты неправильно понял. as это про отношение родитель-потомок, которым упомянутые тобой Int, Long и Char не связаны. Поэтому ты получаешь ошибки. Что это значит: у тебя есть значение, имеющее тип "родитель"и ты хочешь привести его к типу "потомок".
Например:
fun f(n: Number): Int{
return n as Int
}
f(10) // 10
f(10L) // ClassCastException
По второму примеру ты можешь понять, что делать приведение типов, если ты точно не знаешь, какой тип имеет инстанс, чревато ошибками в рантайме.
Поэтому есть форма as?, которая в случае неудачи возвращает null. А также есть оператор is, который проверяет, является ли объект инстансом определенного класса:
val x = 10
x is Int == true
x is Number == true
x is Any == true
x is Long == false
x as Int // redundent
val y: Long? = x as? Long //== null
Кроме того, в котлине есть такая прекрасная вещь, как смарткасты(умное приведение типов). Что это значит. Если компилятор видит, что ты проверил тип значения и это значение не может измениться, то он выводит соответвующий тип сам, и явный каст(as) не нужен. Это работает для условных выражений, if, when, elvis-operator, также если ты ранее делал каст.
val x: Any = ...
if (x is String) x.substring(0, 10)
if (x is Int) x + 10
x as Long // Long или ClassCastException
x + 10L // до этой точки выполнение дойдёт только в случае успешного каста.
И конечно это всё прекрасно работает с пользовательскими типами, божественно вместе с sealed классами:
sealed class B
class D1(val x: Int): B()
class D2(val y: String): B()
object D3: B()
fun f(b: B){
when (b){
is D1 -> b.x
is D2 -> b.y
is D3 -> D3
}
}
Это удобно для возвращения разных значений, обработки ошибок и тд.
Таким образом, использования as имеет смысл в довольно ограниченном числе случаев. Когда компилятор не может доказать что-то, но ты это точно знаешь. Например, в случае мутабельной проперти, или функции, возвращающей одно и то же значение.
Упомянутый тобой toInt это не преобразование типа, а обычная функция, которая возвращает новый тип по неким правилам.