Suppose we have some data classes which are generated with protoc.
Google's toString() sucks for multiple reasons, it's not very human-readable, and even puts line breaks in representation which screws up logs and many tools that don't expect multiline log.
So the problem is: how do we redefine custom toString() for a class like that?
I've tried to override (or maybe hide?) original toString() with an extension function like this
fun Messages.DataClass.toString(): String {
return "some custom logic"
}
but it won't pick up the extended function.
It's not possible to change the implementation of an existing method in a class using Kotlin. An instance method defined in a class always takes priority over an extension function with the same signature.
Related
I'm trying to understand deeply how gradle is working.
Let's take the case of a basic dependencies {} declaration in a build.gradle script.
dependencies is a method on Project object.
DSL Project object = org.gradle.api.Project interface:
void dependencies​(Closure configureClosure)
Configures the dependencies for this project.
This method executes the given closure against the DependencyHandler for this project. The
DependencyHandler is passed to the closure as the closure's delegate.
So: method receives as parameter a configuration closure and establish that closure's delegate object is DependencyHandler class, than execute the closure against it's delegate object.
Having this example:
dependencies {
// configurationName dependencyNotation
implementation 'commons-lang:commons-lang:2.6'
}
This is translated in a call of method add of DependencyHandler class:
Dependency add​(String configurationName, Object dependencyNotation) Adds a dependency to the given configuration.
And now the question:
How exactly is done the translation of the line
implementation 'commons-lang:commons-lang:2.6'
into a class method call (e.g. DependencyHandler.add()) and who is responsable ?
My impression is that there is a missing explanation in the documentation, something like: default method on this delegate object DependencyHandler is add(...), so each configClosure's line, if matching notation configurationName dependencyNotation, will be translate into delegate's object default method.
But this is just a possible interpretation.
Ideea is: I give a closure with multiple lines, this is executed agains a delegation object which happens to have methods add(), and magically for each line this method is called ... how is this happening, based on what mechanism ? Is the Project.dependencies() method doing this, is the delegate object itself, or some other groovy specific mechanisms, etc.
Thank you.
If you want to get deeper insight on how Gradle (or its DSL) work under the hood, you can always check the actual source code. However, since this is not required to understand how to write build scripts, it is not included in the documentation.
Regarding your specific example, I have to admit that I do not exactly know how it is done, but I have a guess. If someone else has better insights, feel free to prove me wrong.
While Gradle indeed uses AST transformations to extend the regular Groovy syntax in some cases (e.g. the task definition syntax), I think they just rely on dynamic methods for dependency definitions.
Groovy is a dynamic language. This includes a method called methodMissing that may be defined by any class and will be called whenever a missing method is called on an object of that class:
class Example {
def methodMissing(String name, args) {
println name
}
}
def example = new Example()
example.method1()
You can find a more detailed example in Mr. Hakis blog.
Since Groovy allows omitting parentheses for method calls with arguments, your example implementation 'commons-lang:commons-lang:2.6' is basically nothing else but calling the method implementation with the dependency notation string as its argument.
Now Gradle could catch these calls via methodMissing and then call DependencyHandler.add() if the configuration actually exists. This allows you to dynamically add configurations in your build script:
configurations {
myConfig
}
dependencies {
myConfig 'commons-lang:commons-lang:2.6'
}
I have a number of methods that can return a string, bool, int or a tuple<int,bool> or if the method fails, returns a class with a numnber of properties in it detailing the in and out params.
Is there a way that when the call returns that I can avoid using Reflection to determine the return types and the values? The code is for low end mobile devices using Xamarin (so both iOS and Android)
No there's no way to traverse through the class properties without reflection.
But As a last resort, you can override the class ToString() method and manually return what you want to return. But you need to be careful as the every time you edit your class you need to update ToString()
I see, that it's defined in Interface IntStream, but when you write IntStream.range(0, 200).sum(); how the implementation is called? where is it? couldn't find..
As for every interface, it's defined in the concrete class(es) that implement the interface.
In this case, it's in java.util.stream.IntPipeline, which is not a public class. But you shouldn't care about that. All you need to know is that an IntStream has that method, which does what the javadoc of the method does.
If you're really curious about its implementation, look in the source code of IntPipeline.java:
return reduce(0, Integer::sum);
Note on how I found out extremely easily: I just open the type hierarchy of IntStream in my IDE (IntelliJ, but all decent IDEs have that functionality), and notice that it has a single direct implementation: IntPipeline, which indeed contains the method.
If you are using an eligible compiler, there is an option to show its implementation. For example, when I want to see its implementation by IntelliJ, I click go to implementation. Then, it redirects.
In IntPipeLine.java,
#Override
public final int sum() {
return reduce(0, Integer::sum);
}
As specified in the docs and seen from the source code, SnakeYAML works with enums by their names. What I'd like to have is to parse values by enum value, e.g.:
Enum:
public enum Strategy {
ALWAYS_RUN("always-run"),
ALWAYS_SKIP("always-skip"),
DEPENDS("depends");
...
}
YAML:
branches:
trunk: always-skip
bugfix: depends
default: always-run
The reason is our code style forces us to use uppercase for enum constants, while I'd like to keep data in the yaml file lowercase.
As far as I am aware, this is not possible. Enum constants are private, and are therefore not accessible by other classes, so the YAML parser would not be able to construct the objects.
Although not perfect, you could use aliases to create a nickname for the enums.
There is another way to do this. Probably it's not clean but works properly.
Create a new Constructor class by extending org.yaml.snakeyaml.constructor.Constructor.
Inside it create a ScalarConstuctor protected class with the same code implementation as in base ScalarConstructor class except of enum parsing implementation.
In a method constructStandardJavaInstance check if an enum exists with uppercase or lowercase name.
Finally create Yaml object with the Constructor (of step 1)
I want to have a class that has a number of fields such as String, Boolean, etc and when the class is constructed I want to have a fieldname associated with each field and verify the field (using regex for strings). Ideally I would just like specify in the constructor that the parameter needs to meet certain criteria.
Some sample code of how :
case class Data(val name: String ..., val fileName: String ...) {
name.verify
// Access fieldName associated with the name parameter.
println(name.fieldName) // "Name"
println(fileName.fieldName) // "File Name"
}
val x = Data("testName", "testFile")
// Treat name as if it was just a string field in Data
x.name // Is of type string, does not expose fieldName, etc
Is there an elegant way to achieve this?
EDIT:
I don't think I have been able to get across clearly what I am after.
I have a class with a number of string parameters. Each of those parameters needs to validated in a specific way and I also want to have a string fieldName associated with each parameter. However, I want to still be able to treat the parameter as if it was just a normal string (see the example).
I could code the logic into Data and as an apply method of the Data companion object for each parameter, but I was hoping to have something more generic.
Putting logic (such as parameter validation) in constructors is dubious. Throwing exceptions from constructors is doubly so.
Usually this kind of creational pattern is best served with one or more factory methods or a builder of some sort.
For a basic factory, just define a companion with the factory methods you want. If you want the same short-hand construction notation (new-free) you can overload the predefined apply (though you may not replace the one whose signature matches the case class constructor exactly).
If you want to spare your client code the messiness of dealing with exceptions when validation fails, you can return Option[Data] or Either[ErrorIndication, Data] instead. Or you can go with ScalaZ's Validation, which I'm going to arbitrarily declare to be beyond the scope of this answer ('cause I'm not sufficiently familiar with it...)
However, you cannot have instances that differ in what properties they present. Not even subclasses can subtract from the public API. If you need to be able to do that, you'll need a more elaborate construct such as a trait for the common parts and separate case classes for the variants and / or extensions.