How to add an "Item" object to an Object ArrayList in kotlin - kotlin

I have the following class
class Item {
var name: String = ""
constructor(n: String) {
name = n
}
}
On my main activity I have declared this:
var list: ArrayList<Object> = ArrayList<Object>()
When I try to do this
list.add(Item("Hey friend"))
The compiler complains about type mismatch (Object -> Item) which is obviously true but since Item is also an Object, shouldn't this be fine? I'm pretty sure you can do this in Java, whats the alternative?
I need the list to be of type object because I have to store different stuff in there, so changing it is not an option.

The supertype of all types in Kotlin is Any, not Object.
Other things you can improve in var list: ArrayList<Object> = ArrayList<Object>():
make it a val because immutability is preferred
If you need an explicit type declaration, use the interface List<Any
use Kotlin's collection builders listOf()
val items: List<Any> = listOf()`
Also, the class definition can be reduced to
class Item(val name: String) //could even be a data class

Related

Filter out null in immutable objects list of fields

I have an immutable object:
class Foo(
val name: String,
val things: List<Thing>
)
A third party lib creates the Foo object with some 'null' Thing objects.
I am creating a new object:
val foo = thirdPartyGetFoo()
val filteredFoo = Foo(foo.name, foo.things.filterNotNull())
That works, however AndroidStudio greys out the filterNotNull function call and presents a warning:
Useless call on collection type: The inspection reports filter-like
calls on already filtered collections.
Is this the right way to filter that list? Should I ignore the warning or is there a better way?
You do not specify what library creates the object with nulls. Some deserialization libraries can use static factory methods which you could configure, and then have the factory method strip the null. For example, if this were Jackson you would simply:
class Foo(val name: String, val things: List<Thing>) {
companion object {
#JsonCreator
#JvmName("createFromNullable")
fun create(name: String, things: List<Thing?>) = Foo(name, things.filterNotNull())
fun create(name: String, things: List<Thing>) = Foo(name, things)
}
}
Then...
val goodFoo = jacksonObjectMapper().readValue<Foo>(someJsonWithNulls)
Maybe your library has options that are similar?
If not, and you don't have 100 of these things with this problem, I would probably create a temporary class to hold the results and convert that to the final class:
open class FooNullable(val name: String, open val things: List<Thing?>) {
open fun withoutNulls(): Foo = Foo(name, things.filterNotNull())
}
class Foo(name: String, override val things: List<Thing>) : FooNullable(name, things) {
override fun withoutNulls(): Foo = this
}
Then you can deserialize into FooNullable and just call withoutNulls() to get the other flavor that is clean. And if you accidentally call it on one without nulls already, it just does nothing.
val goodFoo = Foo("", emptyList<Thing>())
val alsoGoodFoo = goodFoo.withoutNulls() // NOOP does nothing
val badFoo = thirdPartyGetFoo()
val betterFoo = badFoo.withoutNulls() // clean up the instance
val safeFoo = thirdPartyGetFoo().withoutNulls() // all at once!
Not the cleanest, but does work. The downsides is this second step, although it looks like you were already planning on doing that anyway. But this model is safer than what you proposed since you KNOW which type of object you have and therefore you continue to be typesafe and have the compiler helping you avoid a mistake.
You don't have to use inheritance as in the above example, I was just trying to unify the API in case there was a reason to have either version in hand and know which is which, and also act upon them in a similar way.

ArrayList<AbstractObject> adding objects which extended AbstractObject is not possible. How to fix it?

So I have ArrayList<AbstractObject> which is class type abstract. And I have 2 items which extends AbstractObject. If I use abstractList.add(Object1) it says that ArrayList expects object of type AbstractObject and not Object1. I thought that this is possible. Reason why I want to do this is to use multiple objects with 2 different data in single RecyclerView. (ViewTypes)
abstract class ListItem {
abstract val type: Int
companion object {
const val TYPE_HEADER = 0
const val TYPE_ITEM = 1
}
}
class HeaderItem(val headerTitle: String) : ListItem() {
val type: Int
get() = TYPE_HEADER
}
class ObjectItem(val object: ParseObject) : ListItem() {
val type: Int
get() = TYPE_ITEM
}
Init #1:
var recyclerViewArray: ArrayList<out ListItem> = ArrayList()
This is error if I want to add HeaderItem to this list:
Init #2:
var recyclerViewArray: ArrayList<ListItem> = ArrayList()
This says Type mismatch. Tried with as but as is yellowed with message This cast can never succeed.
It is possible and it works fine. If in some expression Kotlin infers the type wrong, you can always specify it manually. In your case
abstractList.add(Object1 as AbstractObject)
Remove out from the declaration of recyclerViewArray and it should work (I just tried it and it ran fine). e.g. I could run this line of code:
recyclerViewArray.add(HeaderItem("test"))
Note that when you remove out you still need to keep the fact that it's an ArrayList of ListItem objects. So you should declare it as:
var recyclerViewArray = arrayListOf<ListItem>()
Some of your code didn't quite compile for me, like having a property called object (I had to put backticks around that) and not putting the override modifier on the type property on HeaderItem.

kotlin custom get immutable list from mutableList

I have a custom getter method for a mutable list to return an immtuable list by using Google's Guava library. And then this mutable list is accessed in the constructor.
data class mutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = mutableListOf()
list.forEach {
mutableList.add(it.copy()) // Exception is thrown here.
// It actually calls its getter method which is an immutable
// list, so when init this class, it throw exception
}
}
}
data class Foo {}
And I decompile it to Java, in the init block, it calls the getter method of mutableList.
Is there a way to call the mutabbleList itself instead of getter method?
Of course it calls the getter (which returns ImmutableList.copyOf(field)).
You can do simply assignment to mutableList new copied mutable list in your init block:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = list.map { it.copy() }.toMutableList()
}
}
or whithout init:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo> = list.map { it.copy() }.toMutableList()
get() = ImmutableList.copyOf(field)
}
Kotlin stdlib opts for interface immutability. That means, the interface an implementation is boxed in determines the mutability of the reference itself.
Therefore, the right way to make a MutableList<T> just a List<T> is to box it, like follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList as List<Int>
That way, being the myImmutableList reference boxed in a List<Int>, it will only expose members from List<Int>, and not those that just MutableList<Int> define, which allow to mutate the state of the object, hence the list.
Then, if you really want to avoid the following issue (resuming from the above code),
val hackedList = myImmutableList as MutableList<Int>
... for which you would be able to access the mutable implementation through unboxing, you may rather be opting for the following solution:
class ImmutableList<T>(list: MutableList<T>) : List<T> by list
fun <T> MutableList<T>.toImmutable() = ImmutableList(this)
And then use it as follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList.toImmutable()
So you'll be avoiding the issue above. Indeed, any attempt to unbox the value return from MutableList<T>.toImmutable() will end up with a TypeCastException, as the implementation of the List<T> is no longer a MutableList<T>. Rather, it is an ImmutableList<T>, which doesn't expose any methods that might mutate the object.
Unlike #Lucas method, this way you won't be wasting time to copy elements, as you'll be relying on the by keyword in Kotlin, which allows you to implement an interface through an already existing implementation. That is, the MutableList<T> you'll be passing to the constructor of ImmutableList<T>.
When I was researching about this topic, the best solution it just worked for me is just enforcing by contract. If you are creating a mutable list, let's say:
val immutableList = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
).toList() // We make it immutable?
and then you use an extension function or any of the recommendations given below, like using ImmutableList.copyOf(field), you might be paying a penalty because you're copying the items into another collection.
Another option is just paying the unboxing cost of doing something like:
val myImmutableList = myMutableList as List<Int>
The solution I opted for is just enforcing by contract, it's a very simple concept. Your MutableList inherits from List. If you want to share a collection of items with that level of abstraction, it's your choice to do it by enforcing the type:
val immutableList: List<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
now if we share that list with another component, we'll use the right abstraction without any cost. We could also have used a Collection, because List inherits from Collection:
val immutableList: Collection<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
For me, using a var instead of val field along with a private setter usually works best
class Order
class Something() {
var orders: List<Order> = listOf()
private set
fun addOrder(order: Order) {
orders = orders
.toMutableList()
.apply { add(order) }
}
}
This exposes it as immutable and requires a single field only. The price we pay is the overhead of creating a new collection when adding elements it.

Kotlin - Transform value at initialisation

i'm new in kotlin and i want to know if we can transform a content value at initialisation : with this example :
#Document
data class Category(
#Id val id: Id? = null,
val label: String
)
Category is a document (entity for mongodb) and when i'm instanciating this object, i want to transform label property in uppercase. How can i do that to stay idiomatic with the language ? The point is to keep the immutable properties of the val keyword.
val categ = Category(label = "Test")
println(categ.label) // --> TEST
Thanks.
You can encapsulate the "upperCasing" into a factory:
data class Category constructor(val label: String) {
init {
if (label != label.toUpperCase()) {
throw IllegalStateException("Label must be uppercase")
}
}
companion object {
fun createInstance(str: String) = Category(str.toUpperCase())
}
}
The init block ensures, that clients don't create unwanted instances with non-upper labels (which should be documented).
Create an instance like this:
val instance = Category.createInstance("xy")
You might want to make explicit that you do transformations if the parameter is not upper case already by naming the factory accordingly, e.g. withTransformedLabel or simply add some documentation ;-)

is it possible to add a template to the getter/setter of a data class?

for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.