Unable to display the age of an object that was passed to a kotlin function - string

I'm just starting to learn kotlin and ran into a problem:
I have a Person class that has two fields
-age (Int data type)
-name (data type String)
there is also a oldUp function where I pass a Person object and increment the object's age field by 10.
Before the end of the program ** I want to display the age of the object that was passed to oldUp **
However, age is not shown.
my code:
class Person(var name: String, var age: Int){
}
fun growOld(human: Person){
human.age+=10
}
fun main(args: Array<String>) {
var human = Person("Kitty",6)
growOld(human)
println(human)
}

If you want to print the age, you can just write: println(human.age).
In your example it might be cleaner to add the growOld method to your class so you can call it on the object. For example:
class Person(var name: String, var age: Int){
fun growOld() {
this.age += 10
}
}
fun main() {
var human = Person("Kitty", 6)
println(human.age) // prints 6
human.growOld()
println(human.age) // prints 16
println(human.name) // prints Kitty
}

The problem is you're trying to print the human object itself. Under the hood, this calls its toString() method - every class has one of these, because it's defined on the type all classes derive from. If you don't override it and provide a nice way to "pretty print" your object, it'll use the default implementation, which is basically a reference to the object in memory.
A lot of classes you use have a nice toString() implementation, e.g. if you print a List you get ["something", "that", "looks", "like", "this"]. But that behaviour needed to be coded in - and you need to do that for your Person class too!
So you can override the default implementation like this:
override fun toString(): String {
// return a String here
}
override means you're taking an existing function and writing your own version of it to use instead - if this doesn't match an existing function you can override, you'll get an error. You'll also get an error if you don't use the override keyword for a function that looks exactly like an existing one in a supertype - it's just to make sure you don't accidentally do the wrong thing. In IntelliJ you can do Ctrl+O to override existing functions if you like.
So you could do something like this:
// inside your Person class
override fun toString(): String {
return "Name: $name, age: $age"
}
and then when you use it in a print statement, or in a string (like "Details: $person" or val details = "Details: " + person) it will call that toString() method and get the string you produced.
Another way to approach this is to use a data class:
data class Person(var name: String, var age: Int)
A data class is a special kind of class where all your "data" goes in the constructor (as properties, either val or var), and then you get some boilerplate stuff for free which uses those properties (and only those properties). Things like an equals() and hashCode() implementation that uses that data - and the relevant thing here, it gives you a toString() implementation that pretty prints name and age. Try it out!
Data classes can be really handy for simple data objects like you have here - but in normal classes, overriding toString() yourself is the general way of doing things. And you can still override a data class's toString if you want - sometimes you might want a more complex representation, or nice formatting, or you might want to only include some properties and ignore others. You're in control of how it prints itself!
And if you just want to print the age property, or print anything at all using the data in your object, then you just need to do what Robin's answer says. You don't need a toString() implementation at all for that (and since this is how you usually use objects, often you won't need to write a toString for your own classes at all)

Related

Is it possible to get the name of variable in Groovy?

I would like to know if it is possible to retrieve the name of a variable.
For example if I have a method:
def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}
If I call this method as follows:
def ordinary = 58
printSomething(ordinary)
I want to get:
ordinary is 58
On the other hand if I call this method like this:
def extraOrdinary = 67
printSomething(extraOrdinary)
I want to get:
extraOrdinary is 67
Edit
I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo
#BeforeTestSuite
def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
KatalonProperties props = new KatalonProperties()
// get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");
//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
String preferedHostname = props.getProperty('GlobalVariable.G_Url')
if (preferedHostname != null) {
GlobalVariable.G_Url = preferedHostname;
WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
} else {
WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
}
//doing the same for other variables is a lot of duplicate code
}
Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:
def setProperty(KatalonProperties props, GlobalVariable var){
WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");
//gets the internal value of var, if it's null then use the one from katalon.features file
GlobalVariable preferedVar = props.getProperty(var.getName())
if (preferedVar != null) {
var = preferedVar;
WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
} else {
WebUI.comment(">>> " + var.getName() + " stays unchanged");
}
}
Here I just put var.getName() to explain what I am looking for, that is just a method I assume.
Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).
I currently don't have a proper dev environment, but here are some pointers:
Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)
Groovy Documentation for Macros
If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the #Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.
Developing AST transformations
This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.
no. it's not possible.
however think about using map as a parameter and passing name and value of the property:
def printSomething(Map m){
println m
}
printSomething(ordinary:58)
printSomething(extraOrdinary:67)
printSomething(ordinary:11,extraOrdinary:22)
this will output
[ordinary:58]
[extraOrdinary:67]
[ordinary:11, extraOrdinary:22]

Alternatives to boxing string field or extending ALL strings in Kotlin?

Given that one can't subclass builtin types in Kotlin, I'm looking for a way to add special method functionality to specific types of string fields in a record, without applying those extension functions to all strings (even within that record type). Say, for example, I have some mutable string fields inside a record, for which I want to define special purpose behaviors.
class Customer {
// ...
var zipcode: String
var email: String
// ...
}
For zipcode, assume I'd like to be able to call
thisCustomer.zipcode.plusFour to get the +4 of the zip code (99999-9999) via regex.
For email, assume I'd like to be able to call
thisCustomer.email.domain to have a regex go get the ...#company.com
portion of the specified email.
My objectives are to:
Avoid the runtime overhead of boxing zipcode into a class Zipcode with just a
single var inside, given that the size of the Customer recordset could potentailly be millions.
Avoid the syntax of having to assign
thisCustomer.zipcode.value = "99999-9999" or thisCustomer.zipcode = Zipcode("99999-9999"), if I did end up having to box the string.
Avoid adding general String.plusFour and
String.domain extension properties, even just within the Customer
class... because zipcodes and emails could technically call each others'
extensions
I've been thinking this over and considered the following options, but struck out with each:
subclassing String: Not possible since strings (and all built-ins)
are closed
applying an interface Zip to the var declaration (var zipcode:
String, Zip): No such syntax I could find
adding inner functions within the getter itself: Syntax doesn't seem to exist for this
using objects or functions-within-functions: Couldn't think of a way
for this to work, although I might not be imaginative enough
Am I missing an obvious solution here? Even if not obvious, with all the syntactic magic of Kotlin, is there a way to make it happen? Maybe there's a way to accomplish some/all of the objectives above without using any of these approaches?
You can use type aliases to make the intention clear:
typealias ZipCode = String
val ZipCode.plusFour get() = ...
typealias Email = String
val Email.domain get() = ...
class Customer {
// ...
var zipcode: ZipCode
var email: Email
// ...
}
Unfortunately, this only clarifies intentions and doesn't stop the compiler from allowing zipcode.domain. But for now I don't think there is any way which satisfies both objectives 1 and 3.
Kotlin developers have decided not to support assignment-incompatible type aliases, which would have fit all your requirements, in favor of waiting for value classes to be available on JVM, as discussed in https://github.com/Kotlin/KEEP/issues/4.
You may create a decorator class using delegate:
class YourString(val value: String) : Comparable<String> by value, CharSequence by value {
val plusFour: String
get() = "" //your logic here
val domain: String
get() = "" //your logic here
override fun toString() = value
}
Usage:
fun String.toYourString() = YourString(this)
class Customer(var zipCode: YourString, var email: YourString)
val customer = Customer("+4 99999-9999".toYourString(), "xxx#company.com".toYourString())
println(customer.zipCode.plusFour)
println(customer.email.domain)

Groovy DSL getting anonymous String assignments

I have a DSL that looks like this:
aMethod {
"a name"
"another name"
"and a third name"
}
My Problem is that I'm unable to access the three string, because calling the closure only returns the last statement. I tried to override the constructor of String(char[] value) which is called when an anonymous String-statement occurs:
def original
// primitive way to get the String(char[])-constructor
String.class.constructors.each {
if(it.toString() == "public java.lang.String(char[])") {
original = it
}
}
// overriding the constructor
String.metaClass.constructor = { char[] value ->
def instance = original.newInstance(value)
// ... do some further stuff with the instance ...
println "Created ${instance}"
instance
}
// teststring to call String(char[] value)
"teststring"
Unfortunately it didn't work and I thought anyway that it is quite complicated.
Thank you for the comments. Actually it would be great to define everything without quotes. But: After having a dsl that can be translated to java objects I'd loved to have additional annotations in my language at development time. I want to annotate duplicate names and so on. The IDE's I know better, Intellij and Eclipse handle Strings "a name" as one PSI-Elements. Splitting these elements can be very inconvinient ... I guess. I think statements in a closure like aMethod {a name} would result in an interpretation like aMethod {a.name}. That would mean that instead of having a StringLiteral Psi "a name", I would have an Object-Psi and a MethodCall-Psi or something like that. I don't know, and my next goal is just "parsing/creating" my java objects. Are you sure that it is impossible to override the String-Constructor?
Is any constructor called when you have a groovy script with this content:
"hello World"

Use groovy categories to add dynamic properties

Expanding on this blog post, I am trying to use a category to create a simple DSL for use with the javax.measure (JSR-275) classes (similar to TimeCategory for time intervals)
However, I do not want to add boilerplate code for each of the possible available methods (getMeter, getMilliMeter, getKelvin, getSecond etc.). I thought overriding the getProperty(String) method would work, but alas, it looks like the getProperty method defined in the category is not used when accessing the property directly.
Here is some simplified code to demonstrate:
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.measure.Measure;
#Category(Number)
class LengthCategory {
public Measure<BigDecimal, Length> getProperty(String unit){
return Measure.valueOf(this,Unit.valueOf(unit));
}
}
use(LengthCategory){
println 3.getProperty("m") // this works
println 3.m // this reports a non-exisiting property
prinlln 3.'m' // as does this
}
Assuming other methods of dynamically adding properties to a runtime object (e.g. Expando, subclassing GroovyInterceptible, mixins and other metaclass manipulations) is not viable and I would really rather not have to manually code getters for every possible unit and SI prefix combination. There are obviously other ways to go about creating a DSL for measurements, but I would still like to understand why this method would not work.
Could someone explain why the getProperty method of the category does not override .propertyName usage? I am obviously missing something important about the resolution of property names using the metaclass during runtime.
I don't know why getProperty doesn't work on categories. But you can define a get method on them that does basically the same (i think). This works:
#Category(Number)
class LengthCategory {
def get(String unit) {
"$this $unit"
}
}
use (LengthCategory) {
println 3.m // 3 m
println 3.'m' // 3 m
}
As far as I can tell, you can't actually extend Integers with full (i.e., readable and writable) properties using Category -- only with methods.
You can extend an Integer using read-only properties by using the method version of the property. You can even make it writable by including a set method. However, there doesn't seem to be a way to store the value passed in other than in a static variable and that ends up affecting all Integers.
Example:
$ cat catprop
#!/usr/local/bin/groovy
#Category(Integer)
class CatInteger {
private static String str = "default"
public static String setN(Integer i, String _str) { str = _str }
public static String getN(Integer i) { return str }
}
use (CatInteger) {
3.n = "333a"
println "3.n is " + 3.n
3.n = "333b"
println "3.n is " + 3.n
4.n = "444"
println "4.n is " + 4.n
println "3.n is " + 3.n
}
$ catprop
3.n is 333a
3.n is 333b
4.n is 444
3.n is 444
$
Note that in the last line 3.n return "444" because the stored field is static. I suppose that one could use a private HashMap and store a value for every Integer accessed, but that's too ugly to contemplate.
Another possibility would be to use the MetaClass Interface's getProperty() and setProperty(). However, I haven't looked into that so I don't know if it would work or not (just a thought).
Nice answer, but not sure, if you's still want to use JSR-275 now that JSR-363 is final?;-)

Vala: Pass String as Class

Scenario:
I have x number of classes. Lets say 10; Each class does different UI Functions. When a user loads a file, that extension tells the program the classname to load; but it's in the form of a string.
Is there anyway to pass a string off as a classname? Something to the effect of.
var classname = "Booger";
var nose = new classname(){ //classname really means "Booger"
//Do Operation
}
You can reflect a type by name using var t = Type.from_name(classname);, however, this works on all types, including enums and structs and it might be the type Type.INVALID. You should probably do some checks, like t.is_a(typeof(MyParentClass)).
You can then instantiate a copy using var obj = Object.new(t);. The whole thing would look like:
var classname = "Booger";
var t = Type.from_name(classname);
if (t.is_a(typeof(MyParentClass)))
return Object.new(t);
else
return null;
It's also worth noting that the run-time type names have the namespace prepended, so you might want to do "MyNs" + classname. You can check in either the generated C or doing typeof(MyClass).name().
I've had the same problem as the OP in regards to getting an assertion error against null. If you take a look at the Glib documentation (in C) it mentions you have to register your class by actually specifying the class name first before you can actually use a string representation of your class name.
In other words you have to use your class first BEFORE you can instantiate a copy of your class with Glib.Type.from_name ("ClassName").
You can use your class first by instantiating a class instance or by getting type information for your class.
var type = typeof (MyClass);
var type_from_string = Type.from_name ("MyClass");
Furthermore, when you use Object.new to create a class there are two things you need to be aware of:
1) You need to cast the return value to get your specific class or base class.
var instance_of_my_class = Object.new (type) as MyClass;
2) Constructors for your class will no longer be called (I don't why). You will need to use the GObject style constructor inside your class:
construct {
pizza = 5;
}

Resources