Dron Bhattacharya
02/23/2023, 10:19 AMclass TList(): MutableList<Some> {
}
TList(Some(), Some()).add(Some())
I have tried things like that shown in this code block but it is not working
I have a list of something. I want it to have a few methods like add, addAll, remove, etc that are already in MutableList.....
If you cannot understand what I am trying to do, please ask. I am open to suggestions on how to approach thisJoffrey
02/23/2023, 10:22 AMclass TList(): MutableList<Some>
, but since MutableList
is just an interface, you would have to implement all the methods yourself. Instead, you could choose to extend an actual implementation of list such as `ArrayList`:
class TList : ArrayList<Some>()
https://pl.kotl.in/cQoA8nFOEJoffrey
02/23/2023, 10:26 AMTList(Some(), Some())
if you don't declare such constructor yourself. With your current declaration (or the one I suggested), you would need to first create an empty list with val list = TList()
and then add stuff to that list.
If you want a constructor that behaves like listOf(...)
, you can add a secondary constructor:
class TList(): ArrayList<Some>() {
constructor(vararg initialElements: Some) : this() {
addAll(initialElements)
}
}
https://pl.kotl.in/Vf2mpjKNXRicardo Rodríguez
02/23/2023, 10:26 AMTList
is going to have some methods that only make sense for a MutableList<Some>
. Maybe you can avoid creating the TList
class and directly extension functions to MutableList<Some>
.Troels Lund
02/23/2023, 10:28 AMvararg
in the primary constructor, if you like:
class TList(vararg e: Some) : ArrayList<Some>(e.asList())
Dron Bhattacharya
02/23/2023, 10:28 AMJoffrey
02/23/2023, 10:29 AMvararg
parameter to the primary constructor too, but if we do that I am suspecting that the vararg
would unnecessarily create an empty array every time the constructor is called, even without any argumentsJoffrey
02/23/2023, 10:31 AM.asList
call it would generate even more object creationsDron Bhattacharya
02/23/2023, 10:32 AMTList()
does not create anything?
But when I do TList(Some())
an array is created.Joffrey
02/23/2023, 10:33 AMaddAll
or asList
.Joffrey
02/23/2023, 10:34 AMMutableList<Some>
, maybe extensions are enough. If you want to limit the number of operations, then you shouldn't extend at all but create an entirely new type instead and list the methods by hand. What's your use case?Dron Bhattacharya
02/23/2023, 10:36 AMclass TaskList(private val tasks: MutableList<Task>) {
val count: Int
get() = tasks.count().also { println("TaskList.count($it)") }
fun addTask(task: Task) = tasks.add(task).also { println("New task added!") }
fun rmTask(task: Task) = tasks.remove(task).also { println("1 task removed!") }
fun findTask(id: Int): Task =
tasks.first { it -> it.id == id }.also { println("Task.id(${it.id}) was searched and found") }
}
Dron Bhattacharya
02/23/2023, 10:38 AMDron Bhattacharya
02/23/2023, 10:46 AMDron Bhattacharya
02/23/2023, 10:53 AMval tasks = mutableListOf<Task>()
and
class TList(vararg task: Task) : ArrayList<Task>() {
init {
addAll(task)
}
}
Which I should I do? And why?Joffrey
02/23/2023, 10:59 AMMutableList<Task>
, so why not use mutableListOf<Task>()
directly?Joffrey
02/23/2023, 10:59 AMList
and MutableList
(filter
, map
, fold
, chunked
, windowed
, and many more...)CLOVIS
02/23/2023, 10:59 AMDron Bhattacharya
02/23/2023, 11:01 AMmutableListOf<Task>()
or TList()
whichever I choose to go withthanksforallthefish
02/23/2023, 11:03 AMclass TaskList(private val tasks: MutableList<Task> = mutableListOf()) : MutableList<Task> by tasks
?thanksforallthefish
02/23/2023, 11:04 AMTaskList(mutableListOf(Some(), Some())).add(Some())
Adam S
02/23/2023, 11:09 AMJsonArrayBuilder
in Kotlinx Serialization
JsonArrayBuilder
is used to build a JsonArray
, which is similar, except the list of JsonElements
is read-only.
The benefit is that the mutable list is private, and can only be modified via one function - fun add(element: JsonElement)
. Having only one function is nice when you’re writing a public library, because it means you can limit how much code you have to maintain!
Having only one add()
function can be annoying to users though, so that’s why there are multiple helper extension functions to make it easier to add functions. Extension functions can’t access private members though, so they don’t risk exposing functionality that you would be locked into maintaining!
But, if you’re writing a small private library, it’s probably nicer just to keep things simple. Maybe you want to migrate to a nice class later - but that’s always possible. My biggest advice would be: don’t get bogged down in paralysis analysis!Dron Bhattacharya
02/23/2023, 11:11 AMthanksforallthefish
02/23/2023, 11:11 AMclass TaskList(private val tasks: MutableList<Task> = mutableListOf()) : MutableList<Task> by tasks {
companion object {
operator fun invoke(varargs tasks: Task) = TaskList(mutableListOf(tasks))
}
}
since invoke
is declared as operator, it can be use as
TaskList(Some(), Some())
Dron Bhattacharya
02/23/2023, 11:14 AMAdam S
02/23/2023, 11:15 AMfactory functions used to create instances of classes can have the same name as the abstract return type
// public interface
interface Foo { /*...*/ }
// private Foo implementation
private class FooImpl : Foo { /*...*/ }
// factory function
fun Foo(): Foo { return FooImpl() }
Dron Bhattacharya
02/23/2023, 11:16 AMDron Bhattacharya
02/23/2023, 11:20 AMmutableListOf()
a factory function?thanksforallthefish
02/23/2023, 11:20 AMclass TaskList(private val tasks: MutableList<Task> = mutableListOf()) : MutableList<Task> by tasks
it has its uses, you have a custom list with all method on lists (which I like, for you have well understood method names instead of custom names like find
) and you can easily add business methods when needed.
I don’t know if there really is an appropriate way of doing things, it is always a trade-off between functionality, readability and performance.Dron Bhattacharya
02/23/2023, 11:24 AMby tasks
part? Your suggestion seems to be most useful for my future use-case.thanksforallthefish
02/23/2023, 11:25 AMDron Bhattacharya
02/23/2023, 11:26 AMDron Bhattacharya
02/23/2023, 1:00 PM*task
syntax?
With IDE suggestion 💡 I have modified the code like this:
class TList(private vararg val task: Task): MutableList<Task> by mutableListOf<Task>(*task) {
}
Is it a unary operator like in python?Joffrey
02/23/2023, 1:03 PM*
is called the spread operator. It allows to use an array in a place where multiple arguments (vararg
) would be expected (it "spreads" the contents of the array as if they were multiple arguments). mutableListOf
expects multiple arguments for the elements of the newly created list, but task
is an array of tasks, received through the vararg param itself (btw, I would really name this tasks
-plural- for this reason)Joffrey
02/23/2023, 1:05 PMDron Bhattacharya
02/23/2023, 1:07 PMJoffrey
02/23/2023, 1:08 PMyou can easily add business methods when needed@thanksforallthefish you can also do that with extensions. This is not part of the benefits of using your own type, and not really a good reason to do so
Joffrey
02/23/2023, 1:08 PMJoffrey
02/23/2023, 1:09 PMI will in the future have some custom functions too. From the conversation in this thread I understood that in that case I have create my own list.Not really. If you want to add operations to mutable lists of `Task`s, you can use extension functions without creating your own type:
fun MutableList<Task>.doSomethingSpecial() {
// ..
}
Dron Bhattacharya
02/23/2023, 1:10 PMJoffrey
02/23/2023, 1:11 PMMutableList<Task>
, you could use a typealias
instead.
If your goal is only to add nice extensions on any (mutable) list of Task
, then use extension functions.Dron Bhattacharya
02/23/2023, 1:16 PMreduce the number of available operationsThis is a necessary requirement. But, in the last code that I shared, creating my own Type is not limiting the number of unnecessary operations. Am I missing something?
Joffrey
02/23/2023, 1:17 PMin the last code that I shared, creating my own Type is not limiting the number of unnecessary operations.Exactly, that's why I added "In that case you would not implement a list interface or extend a list class, so most of the suggestions in this thread would not be what you want" If you want to limit the number of operations, you have to create your own type, but you cannot make it implement the
List
or MutableList
interfaces because by definition that would mean supporting all their operations.Joffrey
02/23/2023, 1:20 PMList
. If you just find those operations unnecessary, you just get them for free by using lists, so it's not a problem per se and you don't need your own type for that.Dron Bhattacharya
02/23/2023, 1:21 PMJoffrey
02/23/2023, 1:23 PM.filter
? Do you have any example of list operation that you want to forbid? (or that would be a mistake to use with your type?)Dron Bhattacharya
02/23/2023, 1:27 PMsort
, addAll
, removeAll
, etc that modify the List in a way the List should not be modified (can cause issues)Dron Bhattacharya
02/23/2023, 1:41 PMJoffrey
02/23/2023, 3:38 PMaddAll
if you allow add
, you just make it less convenient and likely less efficient. This may also be true for removeAll
if you provide a way to iterate your list of tasks. If you don't provide a way to iterate, and just want to provide access by id, maybe you want a Map
instead of a List
(but that means you also give up on order, which would be strange because tasks are usually ordered). It would be interesting to share how you want to use the instances of this type, because there may be an existing data structure meant for your use case.
But yes, overall I understand your point. If you really want to limit the number of operations, or control which exact operations you want to support, you will have to create a new type and define the allowed operations by hand. Then you're free to choose (and even change) the backing data structure(s) that you'll use inside.