In my Kotlin Android project, I made a FileItem class which extends Serializable
class FileItem(<parameters>) : Serializable, Comparable<FileItem> {
So I needed to Serialize instances of this class into a Bundle
val arguments:Bundle = Bundle()
arguments.putSerializable("folders", folders as Serializable)
where folders has been declared as :
folders:Array<FileItem> (method parameter)
The serialization code above compile without any warning. Meanwhile, the problem comes when I need to unserialize folders items :
val arguments: Bundle? = getArguments()
if (arguments != null){
foldersItems = arguments.getSerializable("folders") as Array<FileItem>
where foldersItems is declared as
var foldersItems: Array<FileItem>?
I get the following warning, that I can't manage to solve without suppress_warning annotation :
w: <Path to my class>: (78, 28): Unchecked cast: java.io.Serializable! to kotlin.Array<com.loloof64.android.chess_positions_archiver.main_file_explorer.FileItem>
This kind of code compiles in Java/Groovy without warning (folderItems is then a FileItem[]), so how can I modify the kotlin code for the compiler to be "satisfied" ?
I noticed in official Kotlin documentation that Kotlin Array does not extend Serializable and is not open for inheritance. Is it possible meanwhite to "add" it via a kind of extension method ?
In fact, the cast is not unchecked, the compiler's warning is misleading.
This happens because in Kotlin arrays are represented by generic class Array<T>, and the compiler treats it as usual generic class with type parameters erased at runtime.
But on JVM arrays have reified types, and when you cast something as Array<SomeType>, the generated bytecode really checks the type parameter to be SomeType as well as something being an Array<*>, which would only happen for any other generic class.
This example shows that the array cast is checked:
val a: Any = Array<Int>(1) { 0 }
val i = a as Array<Int>
val d = a as Array<Double> // gets checked and throws ClassCastException
The easiest solution is indeed to #Suppress("UNCHECKED_CAST"), because actually there should not be any warning.
I filed an issue describing the problem in Kotlin issue tracker.
The cast here is unchecked because the compiler here can't ensure the nullability of array's generic type parameter.
Consider the following example:
fun castAsArrayOfString(param: Any) = param as Array<String>
castAsArrayOfString(arrayOf("a")) // is Array<String>, all ok
castAsArrayOfString(arrayOf("a", null)) // is Array<String>, but contains null
So the compiler warns you about potential type safety problems this cast could introduce.
Related
I declared a variable like this:
var G: Array<MutableList<Int>> = Array(0) { ArrayList() }
Kotlin gives me the following error:
Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E> /* = java.util.ArrayList<E> */
Please specify it explicitly.
It means Kotlin can't infer the type for the ArrayList which should be Int. So I add Int explicitly for the ArrayList like following:
var G: Array<MutableList<Int>> = Array(0) { ArrayList<Int>() }
Kotlin says - Remove explicit types arguments
In this case, Kotlin is ambivalent about how to act.
So is it possible to write code without explicitly declaring the type of ArrayList?
As discussed here,
The way it works curretly is that whenver we encounter a collection in Kotlin, we load a Kotlin version of this class (e.g. kotlin.Collection) instead of a Java version (java.util.*). Using the type java.util.Collection leads to a warning from the Kotlin compiler, because Kotlin's type checker is designed to distinguish between read-only and mutable collections.
So you can try to use like this,
var G = arrayOf<MutableList<Int>>()
Moreover, here are some a good stuff to know for you.
Kotlin says - Remove explicit types arguments
Kotlin doesn't (you can see there's no warning in https://pl.kotl.in/7v1h5Yobu). It's probably the IDEA plugin which does. If you look at https://youtrack.jetbrains.com/issues/KT?q=Remove%20explicit%20types%20arguments, you can see there are quite a few false positives. It may be worth checking if yours is actually one of them and posting a new issue if it isn't.
var G = Array<MutableList<Int>>(0) { ArrayList() }
should work without warning from IDEA either.
I have a problem accessing my "_groups" property with the following code:
function mouseDate(scale){
var g = d3.select("#group")._groups[0][0]
var x0 = scale.invert(d3.mouse(g)[0]);
console.log(x0);
}
Result of my console.log:
Selection {_groups: Array(1), _parents: Array(1)}
_groups: Array(1)
0: Array(1)
0: g#group
When I compile the code I have the following error :
D:/Documents/starter-propre-angular4/src/app/pages/graphe-page/graphe.page.ts (782,32): Property '_groups' does not exist on type 'Selection<BaseType, {}, HTMLElement, any>'.
So my question is: Is there a solution to get the information in "_groups" all year round knowing that I am evolving into TypeScript using d3js
The _groups property is a private member of a Selection object and should as such not be accessed directly. (Side note: it is common convention in JavaScript that any member starting with an underscore denotes a private member. See, e.g., "Underscore prefix for property and method names in JavaScript").
Since the property is considered private it is not part of the public interface and is therefore not included in the TypeScript type declaration for the d3-selection module. Hence, you get the compiler error you witnessed. Although this actually will work in pure JavaScript the TypeScript compiler did exactly what it is supposed to do—namely, prevent you from doing some unsafe stuff.
Looking at the code you posted, though, it becomes apparent that you are not interested in the _groups property itself but rather in _groups[0][0] which internally refers to the first node of the selection. Fortunately, the selection.node() method will return exactly that first element. You can do it like this:
function mouseDate(scale){
var g = d3.select("#group").node();
var x0 = scale.invert(d3.mouse(g)[0]);
console.log(x0);
}
there are two situation make me confuse when develop swift 2.2 by using Xcode 7.1, please see the example below, thanks
First, when import Foundation, I declared an testArray which contains two item, an Integer type 1 and a String type "hello", my question is why Swift type inference testArray to Array(NSObject) instead of Array(Any)
import Foundation
let testArray = [1, "hello"]
print(testArray.dynamicType) //testArray is Array<NSObject>
Second, when i remove import Foundation, the code below can't be compile, the error message is "Type of expression is ambiguous without more content", my question is why Swift not type inference to Array(Any) in this situation, thanks for help
let testArray2 = [2, "world"]
print(testArray2)
//can't compile, error message = "Type of expression is ambiguous without more content"
/// The protocol to which all types implicitly conform.
public typealias Any = protocol<>
Any is just a protocol that all types implicitly conform to – it's not a concrete type itself. Swift cannot infer an array of non-concrete types, which is why it fails to infer Any, but succeeds with NSObject (Int can be bridged to NSNumber, String can be bridged to NSString – and they both inherit from NSObject, which is a concrete type).
For example, consider this:
protocol Foo {}
struct Bar:Foo {}
struct Baz:Foo {}
let arr = [Bar(), Baz()] // error: Type of expression is ambiguous without more context
Because Foo is a non-concrete type, Swift cannot infer an array of it. You have to explicitly tell the compiler what you want its type to be:
let arr:[Foo] = [Bar(), Baz()]
You'll also get the same behaviour with AnyObject (as it's a protocol that all classes implicitly conform to – but still not a concrete type):
class Qux {}
class Fox {}
let a = [Qux(), Fox()] // error: Type of expression is ambiguous without more context
let a1:[AnyObject] = [Qux(), Fox()] // no error
Why Swift is unable to infer an array of non-concrete types is most likely due to the existing limitations of non-concrete types in the language – currently concrete types are required for most non-trivial operations. See this great Q&A for an example.
But to be honest, you should really be thinking more about whether you actually need an array of Any. I cannot think of a single practical application of having an array of Any, as because everything implicitly conforms to the elements, they must be guaranteed to do nothing (you can't call a specific method on something that could be anything). Sure you can type-cast, but what's the point in getting back the type safety that you threw away to begin with?
You should always be as type specific as you can. You could build a wrapper for your values – this could either be a simple struct to wrap a couple of properties, or a type erasure in order to wrap non-concrete types in a pseudo concrete type. At the very least, you should consider creating your own protocol that your array elements conform to.
Because it won't auto recognize array of Any
it will work if you define it as
let testArray2 :[Any] = [2, "world"]
the Foundation library imports the NS API, which automatically converts the 2 to NSNumberand "world" to NSString, converting it automatically to array of NSObject
var channelsNumber = track.getNumberOfChannels()
var framesNumber = lastFrame - firstFrame
var frames = Array.ofDim[Int](channelsNumber)(framesNumber)
System.out.println(frames.length);
System.out.println(frames.length);
I try to define two dimensional array of integers. And I get this error:
[error] .../test.scala:58: type mismatch;
[error] found : Int
[error] required: scala.reflect.ClassManifest[Int]
[error] var frames = Array.ofDim[Int](channelsNumber)(framesNumber)
[error] ^
[error] one error found
What is "scala.reflect.ClassManifest[Int]"? Why channelsNumber passes and framesNumber, which is also an integer doesn't?
Now, you are calling method ofDim [T] (n1: Int)(implicit arg0: ClassManifest[T]) which you don't want to. Change the call to Array.ofDim[Int](channelsNumber,framesNumber) and the method ofDim [T] (n1: Int, n2: Int)(implicit arg0: ClassManifest[T]) will be called. You want to leave the implicit parameter group implicit.
And - class manifest is a way how to preserve type information in generic classes.
First your error: ofDim takes all the dimension in a single parameter list. You need
Array.ofDim[Int](channelsNumber, framesNumber)
Second, ClassManifest. Due to type erasure, and the fact than in the JVM, arrays are very much like generics, but are not generic (among other things, no type erasure), the generic method ofDim needs to be passed the type of the elements. That is the ClassManifest, which is close to passing a Class in java (you have to do the same in java -or pass an empty array of the proper type, in Collection.toArray- if you have a generic method that must return an array) This comes as as an implicit arguments, that is there is another parameter list with this argument, but the scala compiler will try to fill it automatically, without you having to write it in the code. But if you give a second parameter list, it means you intend to pass the ClassManifest yourself.
This does not work:
def giveArray[T](elem:T):Array[T] = {
new Array[T](1)
}
But this does:
def giveList[T](elem:T):List[T] = {
List.empty[T]
}
I am sure this is a pretty basic thing and I know that Arrays can behave a bit unusual in Scala.
Could someone explain to me how to create such an Array and also why it doesn't work in the first place?
This is due to JVM type erasure. Manifest were introduce to handle this, they cause type information to be attached to the type T. This will compile:
def giveArray[T: Manifest](elem:T):Array[T] = {
new Array[T](1)
}
There are nearly duplicated questions on this. Let me see if I can dig up.
See http://www.scala-lang.org/docu/files/collections-api/collections_38.html for more details. I quote (replace evenElems with elem in your case)
What's required here is that you help the compiler out by providing some runtime hint what the actual type parameter of evenElems is
In particular you can also use ClassManifest.
def giveArray[T: ClassManifest](elem:T):Array[T] = {
new Array[T](1)
}
Similar questions:
cannot find class manifest for element type T
What is a Manifest in Scala and when do you need it?
About Scala generics: cannot find class manifest for element type T