kotlin's javaclass.isPrimitive fails if parameter compile-time is not actual type - class

The following code
fun foo(value:Double) {
if(!value.javaClass.isPrimitive) {
println("try again")
return
}
}
println("that's nice")
}
fun main() {
foo(0.0)
}
displays:
"that's nice"
but setting value type as Any:
fun foo(value:Any) {
if(!value.javaClass.isPrimitive) {
println("try again")
return
}
println("that's nice")
}
fun main() {
foo(0.0)
}
will display: "try again"
even though valueruntime type is Double,
link for testing : https://pl.kotl.in/HkghkAkF4
quote from https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html:
inline val T.javaClass: Class
Returns the runtime Java class of this object
which from https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isPrimitive should give me right
EDIT: removed unnecessary run{}

When you declare the type as Double, that's a primitive double (rather than the Double wrapper class).
When you declare the type as Any, the method then accepts any object as a parameter, so even if you pass in a primitive double, it will be auto-boxed into a wrapper object for Double and will no longer be a primitive.
You can see this by running this snippet on play.kotlinlang.org:
fun main() {
useDouble(3.0)
useAny(3.0)
}
fun useDouble(value: Double) = println("${value.javaClass.name}")
fun useAny(value: Any) = println("${value.javaClass.name}")
Prints:
double
java.lang.Double

isn't there a method like "isPrimitive" for wrapper classes
Not directly, but you can check whether a value belongs to a wrapper class by
value::class.javaPrimitiveType != null
Or if you just have a clazz: Class<T>,
clazz.kotlin.javaPrimitiveType != null

Related

Make initializer generic without type in function signature

I have broken down a more complex matter to this class. This way it doesn’t make sense, but it is easier to talk about:
class GenericClass<Type: Any> {
var object: Type?
enum DataType {
case string, integer
}
init(dataType: DataType) {
switch dataType {
case .string:
object = "string" // Cannot assign value of type 'String' to type 'Type'
case .integer:
object = 1 // Cannot assign value of type 'Int' to type 'Type'
default:
object = nil
}
}
}
How can I make this initializer infer the type Type when there is no reference in the function signature?
I asked a related question before (probably with to much cluttered detail): Make Generic Class Codable
Not sure if this answers your question but this is how you are supposed to create an initializer for a generic structure/class without a specific type in the function signature (generic initializer):
class GenericClass<T> {
let object: T
init(_ object: T) {
self.object = object
}
}
let stringClass = GenericClass("string")
print(stringClass.object)
let intClass = GenericClass(1)
print(intClass.object)
This will print
string1
The actual goal/benefit of generics is to get rid of type checks at runtime.
Referring and in addition to Leo's answer if you want to constrain the types to String and Int do it at compile time
protocol GenericClassCompatible {}
extension String : GenericClassCompatible {}
extension Int : GenericClassCompatible {}
class GenericClass<T : GenericClassCompatible> {
let object: T
init(_ object: T) {
self.object = object
}
}
If the type in the init method is not String or Int you'll get an error at compile time.

Cannot convert value type... when using generics and polymorphism in Swift 3

In my simplified example I'm getting the error: Cannot convert value of type 'Foo' to expected argument type BaseItem<Any>
But the class Foo extends BaseItem<String>.
This is the example code:
class BaseItem<T> {
var param: T?
}
class Foo: BaseItem<Int> {
}
func checkItem(item: BaseItem<Any>) -> Bool{
return item.param != nil;
}
I get the error when calling
checkItem(item: Foo())
What am I missing?
You need to define your checkItem function in terms of generics too:
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
Gotta define checkItem function with generics too.
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
The problem is that generics are invariant – consider if your checkItem(item:) function had said:
func checkItem(item: BaseItem<Any>) {
item.param = "foo"
}
That would be illegal for a BaseItem<Int>, as you cannot possibly assign a String instance to an Int? property – which is why it (an instance of Foo) cannot be typed as a BaseItem<Any>.
The solution, as other answers have said, is to use a generic placeholder for the function:
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
Now, rather than saying that you're taking a BaseItem<Any>, that has a param of type Any? (can be assigned a value of any type) – you're now saying that you're taking a BaseItem with any specific placeholder type; which will be satisfied at the call-site of the function.
The function implementation itself therefore cannot make any assumptions about this type, and will disallow the assignment of an arbitrary value to param. The compiler will only allow an assignment of a value of type T.
The signature of checkItem function should be: checkItem<T>(item: BaseItem<T>) -> Bool, as follows:
func checkItem<T>(item: BaseItem<T>) -> Bool {
return item.param != nil
}
Usage:
checkItem(item: Foo()) // false
let myFoo = Foo()
myFoo.param = 0
checkItem(item: myFoo) // true
The reason of why the compiler complains about
Cannot convert value of type 'Foo' to expected argument type
BaseItem
is that you are trying to pass BaseItem<Int> instance as BaseItem<Any> which is invalid (Any data type is not T generic type).

How to pass a class as an function argument in scala?

The problem is the following:
There is a module with such function
def testExceptions(toTest: MyClass): Unit = {
val isException = try {
toTest.increaseValue(-200);
false
} catch {
case e: Exception => true
}
if (isException) {
// some actions
} else {
// another actions
}
}
This function is importing in the file where MyClass is defined. It has such view
class MyClass(var value: Int) {
def show(): Unit = {
println(s"Value: $value")
}
def increaseValue(dv:Int): Unit = {
if(dv <= 0) { throw new IllegalArgumentException }
else { value += dv }
}
}
But I cannot pass MyClass object to the module function testExceptions because it is undefined in the file where this function exists. If I define MyClass in this file, it will be used in the testExceptions.
I will be grateful any help solving my confusion.
How to pass a class as an function argument in scala?
You can't. You can only pass objects as arguments. Classes are not objects, classes are types.
You can use reflection to get a proxy object that represents a type and pass that as an argument, though.
But I cannot pass MyClass object […]
That is because MyClass is not an object, it is a class. You cannot pass a class or any other type as an argument, only objects.
to the module function testExceptions
Note that testExceptions is defined to take an argument that is a value of type MyClass, it does not take MyClass itself as an argument. You need to instantiate an object of MyClass and pass that as an argument.

Swift, classes based on extended protocol don't conform to original protocol

These protocols are giving me nightmares.
I am trying to implement a couple protocols and classes conforming to them, such that I can have default implementations, but customized implementations are available by extending the protocols/classes. So far, this is what I have:
protocol ProtA {
var aProperty: String { get set }
var anotherProperty:String { get set }
func aFunc (anArgument: String) -> String
}
protocol ProtB: ProtA {
var aThirdProperty: String { get set }
}
protocol ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA)
}
class ClassA: ProtA {
var aProperty: String = "Hello, I'm a String."
var anotherProperty: String = "I'm not the same String."
func aFunc (anArgument: String) -> String {
return anArgument
}
}
class ClassB: ProtB {
var aProperty: String = "Hello, I'm a String."
var anotherProperty: String = "I'm not the same String."
var aThirdProperty: String = "I'm yet another String!"
func aFunc (anArgument: String) -> String {
return anArgument
}
}
class ClassC: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aProperty) // Works fine.
}
}
Then, if I do
class ClassC: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aProperty) // Works fine.
}
}
But, if I do
class ClassD: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aThirdProperty) // Value of type 'ProtA' has no member 'aThirdProperty'
}
}
and, if instead I do
class ClassE: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtB) {
print (anotherParameter.aThirdProperty) // Type 'ClassE' does not conform to protocol 'ProtC'
}
}
What am I doing wrong?
The problem
When inheriting from a type, you cannot narrow down the types of the parameters used in overridden functions. This is what you've done by changing the parameter from type ProtA (a more general type), to ProtB (a more specific type).
This is a consequence of the Liskov substitution principle. Simply put, a subclass must be able to do (at minimum) everything that the superclass can do.
ProtC establishes that all conforming types have a function func doSomething(parameter: Int, with anotherParameter: ProtA), with type (Int, ProtA) -> Void).
Your modified function in ClassE has type (Int, ProtB) -> Void. However, this function can no longer act as a substitute for the one it overrides.
Suppose that it was possible to do what you tried. Watch what would happen:
let instanceConformingToProtA: ProtA = ClassA()
let instanceConformingToProtC: ProtC = ClassE()
// This would have to be possible:
instanceConformingToProtC(parameter: 0, amotherParameter: instanceConformingToProtA)
But, ClassE() can't take instanceConformingToProtA as a valid argument to its second parameter, because it's a ProtA, not the required ProtB.
The solution
The solution to this problem is entirely dependant on what you're trying to achieve. I would need further information before being able to proceed.
As a rule of thumb, when overriding inherited members:
Parameter types must be the same, or more general.
E.g. you can't override a function with a parameter of type Car, and change the parameter type to RaceCar. Doing so breaks your classes ability to work with RaceCars, which it must be able to do by the LSP.
E.g. you can override a function with a parameter of type Car, and change the parameter to Vehicle. Doing so preserves your classes' ability to work with `Vehicles.
Return types must be the same, or more specific.
E.g. you can't override a function with a return type of Car with a function that returns Vehicle. Doing so would mean the returned value is "less powerful" than the super class guarantees it should be.
E.g. you can override a function with a return type of Car with a function that returns RaceCar. Doing so would mean the returned value is "more powerful", and it does at least as much as what the super class guarentees.
There's nothing wrong. You should just make sure that the declarations are semantically consistent.
You should either create ProtD declaring the method with a ProtB parameter OR unwrapping the gotten ParamA parameter to use it as ProtB.
func doSomething(parameter: Int, with anotherParameter: ProtB) {
if let a = anotherParameter as? ProtA {
print (a.aThirdProperty)
}
}

How to initialize a field by calling a method

The following class refuses to compile:
class InitTest { // Class 'InitTest' must either be declared abstract
// or implement abstract member 'v: Int'
var v: Int
def int(v : Int) = {
this.v = v
}
}
I was kind of surprise by that we can't just leave values "uninitialized". In Java, it would be assigned with null. In Scala, it does not compile. How to do this in Scala?
You can do this:
class InitTest {
var v: Int = _
def int(v : Int) = {
this.v = v
}
}
Since v has a value type, there is no way of assigning null to it. However, Scala lets you use _ to represent the "zeroed" value. For numbers, that is zero and for pointers that is null. Good way of representing uninitialized values.