Reasoning:
Hello guys. I'm building an evolution simulator as personal project. I have a few parameters set on textfields, such as the speed of the simulation and the number of "organisms". These are going to be accessed by multiple components of the application. Because i would also like to use validation on a few parameters, I set up a ViewModel like such:
class ParametersModel: ViewModel() {
// These properties would likely be DoubleProperty, but for simplicity lets pretend they are strings
val simulationSpeed = bind { SimpleStringProperty() }
val organismsGenerated = bind { SimpleStringProperty() }
}
... and then perform the validation tests on the textfields:
val model = ParametersModel()
textfield(model.simulationSpeed).required()
This works alright, but the issue with it is that I'm defining the model properties as a bind to an empty SimpleDoubleProperty, which is redundant since I'm never commiting this model (the program should always read changes as they are typed). At the same time, I cant define the model properties as simply:
class ParametersModel: ViewModel() {
val simulationSpeed = SimpleStringProperty()
val organismsGenerated = SimpleStringProperty()
}
Because I then get an error about the validation:
The addValidator extension can only be used on inputs that are already bound bidirectionally to a property in a Viewmodel. Use validator.addValidator() instead or make the property's bean field point to a ViewModel.
The other option I could take would be to make a class named something like GlobalProperties, which would keep my properties and also a ValidationContext. I could then add validators by using validationContext.addValidator and pass the textfields. But at this point I feel I'm just coding a ViewModel equivalent.
Question:
Is ViewModel the correct way of keeping "globally" accessed parameters set by textfields? If so, is there a way to not have to set the model properties as a bind of an empty one, since i dont ever need to commit anything?
Usually you would use a ViewModel with some sort of model. Then you can use the ViewModel to handle user input, which stores the current state of the user input, and the backing model will only be update when the ViewModel is committed, assuming validation passes (which seems at odds with your claim that you "dont ever need to commit anything").
Something like this:
class Parameters {
val simulationSpeedProperty = SimpleStringProperty(...)
var simulationSpeed by simulationSpeedProperty
val organismsGeneratedProperty = SimpleStringProperty(...)
var organismsGenerated by organismsGeneratedProperty
}
class ParametersModel(parameters: Parameters): ItemViewModel<Parameters>(parameters) {
val simulationSpeed = bind(Parameters::simulationSpeedProperty)
val organismsGenerated = bind(Parameters::organismsGeneratedProperty)
}
Then you can be sure that the Parameters backing the ParametersModel always has valid values in it (assuming of course it was initialized with valid values).
Related
I'm trying to get the type of some class properties in order to strongly typing my Kotlin Code.
In typescript, we can do this (stupid examplebut this is to explain)
class Test {
private _prop:string
constructor(val:Test["_prop"]){
this._prop = val
}
public get prop():Test["_prop"] { return this._prop}
}
const t:Test["_prop"] = "fdds"
The benefit here is that if I need to chnange the type of "_prop", no need to refactor the whole code, as the type is find thanks to Test["_prop"].
Is there a way to do this in Kotlin ?
I've seen reflection functions in Kotlin, but can't get what I want
Kotlin code :
class Test(val prop:Int) {
fun ppr() {
println(prop)
}
fun getProp():Int {
return prop
}
}
fun main() {
println("Hello, world!!!")
var t:Test = Test(4)
t.ppr()
var a:Int = t.getProp() // how to change :Int by "return type of func Test.prop
}
What you're trying to do is the opposite of strong typing. The point of a strong-typed system is that you're defining exactly what things are, and the system requires you to interact with those things correctly, and prevents you from doing things those types don't support
You're working with specific types and defined type hierarchies, and the way you can interact them is strongly enforced. It's possible to go outside the type system, e.g. with unchecked casts, or by reflection (which can get close to throwing the whole thing out completely) - but that's losing the benefits of strong typing, the guarantees and assistance it can provide, and makes errors a lot more likely
Basically if you want to change the type, you're supposed to refactor it. That lets the system handle it all for you systematically, and it will point out any problems that change might introduce, so you can resolve and handle them. This is another benefit of a strongly typed system - it can help you in this way
If you want to stay within the type system, but just want to update a type and avoid creating changes in a bunch of files, then #Sweeper's typealias approach will work - kinda abstracting a type definition away to one place (and you can give it a more meaningful name that doesn't reflect the specific type it happens to be right now). But if you meaningfully change what that underlying type is, your code will probably have to handle it anyway, unless you're just doing a common call on it like toString().
I might have got what you're asking for wrong, but I wanted to point this stuff out just in case, since you were talking about reflection and all!
You can't do it exactly like that in Kotlin, but you can declare a type alias, which sort of achieves the same result - enabling you to change the type of multiple things by editing only one place.
typealias PropType = Int
class Test(val prop: PropType) {
fun prop(): PropType {
return prop
}
}
To change the type of both, just change the typealias PropType = Int line.
However, note that you don't actually need to do this if you just want to write a getter. You don't need to explicitly write getters if all it does is just returning the property's value. If you want to do something extra in the getter, you can do:
class Test(prop: Int) {
val prop = prop
get() {
// do something extra in the getter
println("getting prop!")
return field // return the underlying field
}
}
The getter will be called whenever you access Test.prop, and again, you only need to change one place to change the type of the property.
I have stumbled upon a behaviour in TornadoFx that doesn't seem to be mentioned anywhere (I have searched a lot) and that I'm wondering about.
If I define a view like this with the TornadoFx builders for the labels:
class ExampleView: View() {
override
val root = vbox{ label("first label") }
val secondLabel = label("second label")
}
The result is:
That is, the mere definition of secondLabel automatically adds it to the rootof the scene.
However, if I place this definition BEFORE the definition of root...
class ExampleView: View() {
val secondLabel = Label("second label")
override
val root = vbox{ label("first label") }
}
... or if I use the JavaFx Labelclass instead of the TornadoFx builder ...
class ExampleView: View() {
override
val root = vbox{ label("first label") }
val secondLabel = Label("second label")
}
... then it works as I expect:
Of course, I can simply define all variables in the view before I define the rootelement but I'm still curious why this behaviour exists; perhaps I am missing some general design rule or setting.
The builders in TornadoFX automatically attach themselves to the current parent in the scope they are called in. Therefore, if you call a builder function on the View itself, the generated ui component is automatically added to the root of that View. That's what you're seeing.
If you really have a valid use case for creating a ui component outside of the hierarchy it should be housed in, you shouldn't call a builder function, but instead instantiate the element with it's constructor, like you did with Label(). However, the use cases for such behavior are slim to none.
Best practice is to store value properties in the view or a view model and bind the property to the ui element using the builders. You then manipulate the value property when needed, and the change will automatically update in the ui. Therefore, you very very seldom have a need to access a specific ui element at a later stage. Example:
val myProperty = SimpleStringProperty("Hello world")
override val root = hbox {
label(myProperty)
}
When you want to change the label value, you just update the property. (The property should be in an injected view model in a real world application).
If you really need to have a reference to the ui element, you should declare the ui property first, then assign to it when you actually build the ui element. Define the ui property using the singleAssign() delegate to make sure you only assign to it once.
var myLabel: Label by singleAssign()
override val root = hbox {
label("My label) {
myLabel = this
}
}
I want to stress again that this is very rarely needed, and if you feel you need it you should look to restructure your ui code to be more data driven.
Another technique to avoid storing references to ui elements is to leverage the EventBus to listen for events. There are plenty of examples of this out there.
In the Android docs it shows an example creating a LiveData object as follows:
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
But I have seen code elsewhere that shows it like this:
val currentName: MutableLiveData<String> = MutableLiveData()
Both of these are located in the viewmodel. In the second example, the LiveData model is instantiated when the class is created whereas in the first example, it is only instantiated when the object is first used.
Are both of these cases valid?
Yes, both of these cases are valid. However, there is a distinct difference between the two. When using by lazy it will still set the LiveData object, but it will not set it until the variable is first used. In the case of the second option, it will initialize the LiveData object when parent is created.
Im new to Kotlin and wonder what does the get() = login_email.txt.toString() do?
Does it set email String?
get() and set(value) after a field means the declaration of a custom getter and/or setter. Here's a basic example using default values:
class Demo{
var something: String
get() = field
set(value) {
field = value;
}
constructor(something: String){
this.something = something;
}
}
These two are, however, redundant. You don't actually need them unless you're doing something custom with it. They're automatically added for vars, though that only applies to getters for vals (because they can't be changed, they don't have setters).
The line you were asking about is a custom getter.
get() // declares a custom getter
= // if you don't know how this works, see my explanation below
login_email.text.toString() // you should be familiar with this part already; gets the string value of the field
If you're not familiar with the syntax, this is the equivalent without =:
get(){
return login_email.text.toString()
}
if you have a single return, you can replace the brackets and return keyword with =. If it helps you remember, just remember the alternative to using = (a body + the return keyword)
TL;DR: it declares a custom setter that returns the value of a TextView/EditText (not sure which it is, you didn't include that in the question)
In your case, you're using a custom getter or setter to handle property data. The fields themselves don't actually contain any data, but you have getters for a different object.
Take this as an example:
class Demo(private val someObject: MyCustomObjectWithSomeData){
val text: String
get() = someObject.text
... same for other stuff. Could also have setters, if the properties are mutable
}
Here the object is private, but it could be public or internal for that matter.
Kotlin supports quite a lot with custom getters. For an instance, you can declare a field to display specific fields of a private variable. For an instance, in your case, you have the email. It doesn't need to be a variable, since you have a custom getter, and the field isn't initialized. If you change var email to a val, you can make it non-null:
val email: String
get() = login_email.text.toString()
That also helps with null-safety.
And for the error field, it's slightly more complicated. It can't be a val because you declare a custom setter, but if you add a getter, you can make it non-null:
var error: String
get() = login_error.text.toString()
set(value){
login_error.text = value;
}
Short Answer
get() is used to define a custom getter method. Anytime you access the property you are using the custom getter method
Long Answer
So before we can talk about get(), it is important that we get a proper understanding of what properties actually are.
Properties
We all know that in object oriented programming the main idea of a class is to encapsulate data and code that works on data in a single class. In a language like Java (don't worry we will get back to Kotlin soon), the data of a class is stored in private fields, we then use accessor method (getters and setters) to access the data. In Java the combination of accessor methods and a field is called a property
Now in Kotlin does things a little differently, it entirely replaces the traditional idea of defining accessor methods and fields. By using the val or var keyword Kotlin will automatically generate the corresponding field and appropriate accessor methods for us.
get()
There will come a time when either your code or someone else's code needs a more robust solution to the automatic accessor methods created by Kotlin. This is where get() and set() come into play. By using get() you are defining your own custom accessor method(getter for get()) to be used when you are accessing this property.
I would also like to point out that since val is immutable it does not allow you to define a set() method, only a get()
If I am modeling my value objects using Kotlin data classes what is the best way to handle validation. Seems like the init block is the only logical place since it executes after the primary constructor.
data class EmailAddress(val address: String) {
init {
if (address.isEmpty() || !address.matches(Regex("^[a-zA-Z0-9]+#[a-zA-Z0-9]+(.[a-zA-Z]{2,})$"))) {
throw IllegalArgumentException("${address} is not a valid email address")
}
}
}
Using JSR-303 Example
The downside to this is it requires load time weaving
#Configurable
data class EmailAddress(#Email val address: String) {
#Autowired
lateinit var validator: Validator
init {
validator.validate(this)
}
}
It seems unreasonable to me to have object creation validation anywhere else but in the class constructor. This is the place responsible for the creation, so that is the place where the rules which define what is and isn't a valid instance should be. From a maintenance perspective it also makes sense to me as it would be the place where I would look for such rules if I had to guess.
I did make a comment, but I thought I would share my approach to validation instead.
First, I think it is a mistake to perform validation on instantiation. This will make the boundary between deserialization and handing over to your controllers messy. Also, to me, if you are sticking to a clean architecture, validation is part of your core logic, and you should ensure with tests on your core logic that it is happening.
So, to let me tackle this how I wish, I first define my own core validation api. Pure kotlin. No frameworks or libraries. Keep it clean.
interface Validatable {
/**
* #throws [ValidationErrorException]
*/
fun validate()
}
class ValidationErrorException(
val errors: List<ValidationError>
) : Exception() {
/***
* Convenience method for getting a data object from the Exception.
*/
fun toValidationErrors() = ValidationErrors(errors)
}
/**
* Data object to represent the data of an Exception. Convenient for serialization.
*/
data class ValidationErrors(
val errors : List<ValidationError>
)
data class ValidationError(
val path: String,
val message: String
)
Then I have a framework specific implementations. For example a javax.validation.Validation implementation:
open class ValidatableJavax : Validatable {
companion object {
val validator = Validation.buildDefaultValidatorFactory().validator!!
}
override fun validate() {
val violations = validator.validate(this)
val errors = violations.map {
ValidationError(it.propertyPath.toString(), it.message)
}.toMutableList()
if (errors.isNotEmpty()) {
throw ValidationErrorException(errors = errors)
}
}
}
The only problem with this, is that the javax annotations don't play so well with kotlin data objects - but here is an example of a class with validation:
import javax.validation.constraints.Positive
class MyObject(
myNumber: BigDecimal
) : ValidatableJavax() {
#get:Positive(message = "Must be positive")
val myNumber: BigDecimal = myNumber
}
Actually, it looks like that validation is not a responsibility of data classes. data tells for itself — it's used for data storage.
So if you would like to validate data class, it will make perfect sense to set #get: validation on arguments of the constructor and validate outside of data class in class, responsible for construction.
Your second option is not to use data class, just use simple class and implement whole logic in the constructor passing validator there
Also, if you use Spring Framework — you can make this class Bean with prototype scope, but chances are it will be absolutely uncomfortable to work with such kind of spaghetti-code :)
I disagree with your following statement :
Seems like the init block is the only logical place since it executes after the primary constructor.
Validation should not be done at construction time, because sometimes, you need to have intermediate steps before getting a valid object, and it does not work well with Spring MVC for example.
Maybe use a specific interface (like suggested in previous answer) with a method dedicated to executing validation.
For the validation framework, I personnaly use valiktor, as I found it a lot less cumbersome that JSR-303