Value get is not a member of java.io.Serializable - scala

I have written 2 codes .The functionality of both the code is same.Both the codes take user data then store it in map and on providing keys we get correspoding user data. I have written an extra logic in code2, whic I have mentioned below.
Code1:
class user(var name:String,var id:Int, var gender:Option[String])
{
override def toString="("+ name+","+id+","+gender+")"
}
object a
{
def main(args:Array[String]):Unit={
var a=new user("kl",90,Some("Male"))
println(a.name,a.id,a.gender)//ACESS VALUES
//DEFINING MAP
var mm=Map(1-> new user("jh",189,Some("Male")),2->new user("gh",12,None),3
->new user("io",100,Some("Female")))
// defining method giving o/p value of specific key of mm
def getkey(i:Int)=
{ mm.get(i)
}
var u1=getkey(readLine("ENTER THE KEY").toInt) // some[user]
println(u1.getClass.getName)
if(u1.isDefined)
{
println(u1.get+","+u1.get.name+","+u1.get.id+","+u1.get.gender)
}
}
}
Code1 1 works properly and O/P is right. I have added extra logic in Code2. The extra logic is getKey method. I have written a code for checking whether the input key is present in map. There I am getting an error:
**value get is not a member of java.io.Serializable**_
Code2:
class user(var name:String,var id:Int, var gender:Option[String])
{
override def toString="("+ name+","+id+","+gender+")"
}
object a
{
def main(args:Array[String]):Unit={
var a=new user("kl",90,Some("Male"))
println(a.name,a.id,a.gender)//ACESS VALUES
//DEFINING MAP
var mm=Map(1-> new user("jh",189,Some("Male")),2->new user("gh",12,None),3-> new user("io",100,Some("Female")))
// defining method giving o/p value of specific key of mm
def getkey(i:Int)=
{
//EXTRA LOGIC
var a=(mm.keys).toList
if(a.contains(i)){mm.get(i)}
else {"NO SUCH ELEMENT EXCEPTION , KEY DOESNT MATCH"}
}
print("ENTER THE KEY \n")
var u1=getkey(1) // some[user]
println(u1.get)
}
}
ERROR -
enter code here
eg1.Option.scala:27: error: value get is not a member of
java.io.Serializable
println(u1.get)
^
one error found
Why does the seriliazable errors occurs in Code2 and not in Code1? Is the error due extra logic in Code2? How to fix an error?
Thank you!

It happens because your getKey function return type is io.Serializable.
Reason for this is that every branch of your if expression is returning a different type:
def getkey(i:Int) = { // io.Serializable
//EXTRA LOGIC
var a=(mm.keys).toList
if(a.contains(i)) { mm.get(i) } // option here
else { "NO SUCH ELEMENT EXCEPTION , KEY DOESNT MATCH" } // string here
}
Consider rewriting your function, so its return type is Option[User], one way of doing so is:
def getkey(i:Int): Option[user] = {
//EXTRA LOGIC
var a=(mm.keys).toList
if(a.contains(i)) { mm.get(i) }
else { None }
}
However, there is no need for checking keys, you can simplify this function to:
def getkey(i:Int): Option[user] = {
//EXTRA LOGIC
m.get(i)
}
Hint: write expected return type for functions to see what's going wrong in such cases.

Related

Instance method 'drive' requires the types 'NotificationItem' and '[NotificationItem]' be equivalent

I have create a class called notification Item and parsing the data from model class RTVNotification
import Foundation
import RTVModel
public class NotificationItem: NSObject {
public var id: String
public var title: String
public var comment: String
public var publishStartDateString: String
init(id: String,
title: String,
comment: String,
publishStartDateString: String) {
self.id = id
self.title = title
self.comment = comment
self.publishStartDateString = publishStartDateString
super.init()
}
}
extension NotificationItem {
static func instantiate(with notification: RTVNotification) -> NotificationItem? {
return NotificationItem(
id: notification.id,
title: notification.title,
comment: notification.comment,
publishStartDateString: notification.publishStartDateString)
}
}
ViewModel
public class SettingsViewModel: ViewModel {
var item = [NotificationItem]()
public var fetchedNotifications: Driver<NotificationItem> = .empty()
public var apiErrorEvents: Driver<RTVAPIError> = .empty()
public var notificationCount: Driver<Int> = .empty()
public func bindNotificationEvents(with trigger: Driver<Void>) {
let webService: Driver<RTVInformationListWebService> = trigger
.map { RTVInformationListParameters() }
.webService()
let result = webService.request()
apiErrorEvents = Driver.merge(apiErrorEvents, result.error())
notificationCount = result.success().map {$0.informationList.maxCount }
fetchedNotifications =
result.success()
.map {$0.informationList.notifications}
-----> .map {NotificationItem.instantiate(with: $0)}
}
}
Getting an Error saying that Cannot convert value of type '[RTVNotification]' to expected argument type 'RTVNotification'
What can i do to solve this.
The purpose of the map() function is to iterate over the elements of an input array and apply a transform function to each of those elements. The transformed elements are added to a new output array that is returned by map(). It's important to understand that the length of the output array is the same length as the input array.
For example:
let inputArray = ["red", "white", "blue"]
let outputArray = inputArray.map { $0.count } // outputArray is [3, 5, 4]
In your code, you are calling:
result.success().map { $0.informationList.notifications }
I don't know RxSwift at all, so I'm going to go into wild speculation here.
First, I don't know exactly what result.success() returns, but the fact you can call map() on it implies result.success() returns an array (which is weird, but ok we'll go with it).
Second, we know the array returned by result.success() contains elements that have an informationList property, and the informationList property has a property called notifications. My guess is that notifications, being plural, means the notifications property type is an array, probably [RTVNotification].
So this code:
result.success().map { $0.informationList.notifications }
Transforms the success() array into a new array. Based on my assumption that notifications is of type [RTVNotification], and further assuming the success() array contains only one element, I would expect the result of
result.success().map { $0.informationList.notifications }
To be an array of type [[RTVNotification]], i.e. an array with one element, where that element is an array of RTVNotifications.
You then feed that [[RTVNotification]] array into another map() function:
.map { NotificationItem.instantiate(with: $0) }
Recall from the start of this answer that map() iterates over the elements of arrays. Since the input array to this map is [[RTVNotification]], its elements will be of type [RTVNotification]. That's what the $0 is in your call - [RTVNotification]. But the instantiate(with:) function takes an RTVNotification, not an array of RTVNotification, thus you get the error:
Cannot convert value of type '[RTVNotification]' to expected argument type 'RTVNotification'
So what can you do to fix it?
I would probably do something like this (you'll have to tailor it to your use case):
guard let successResponse = webService.request().success().first else {
print("no success response received")
return nil // basically report up an error here if not successful
}
// get the list of notifications, this will be type [RTVNotification]
let notifications = successResponse.informationList.notifications
// Now you can create an array of `[NotificationItem]` like you want:
let notificationItems = notifications.map { NotificationItem.instantiate(with: $0) }
// do something with notificationItems...
The caveat to the above is if you need to iterate over each element in the success() array, then you could do that like this:
let successResponses = webService.result().success()
// successNotifications is of type [[RTVNotification]]
let successNotifications = successResponses.map { $0.informationList.notifications }
// successItems will be of type [[NotificationItem]]
let successItems = successNotifications.map { notifications in
notifications.map { NotificationItem.instantiate(with: $0) }
}
In other words, in this last case, you get back an array that contains arrays of NotificationItem.
Your problem is here:
fetchedNotifications: Driver<NotificationItem> should be fetchedNotifications: Driver<[NotificationItem]> and the line .map {NotificationItem.instantiate(with: $0)} needs another map You are dealing with an Observable<Array<RTVNotification>>. You have a container type within a container type, so you need a map within a map:
.map { $0.map { NotificationItem.instantiate(with: $0) } }
When your types don't match, you need to change the types.
Other issues with your code...
Drivers, Observables, Subjects and Relays should never be defined with var, they should always be lets. Objects that subscribe to your properties before the bind is called will connect to the .empty() observables and never get any values. This is functional reactive programming, after all.
Your NotificationItem type should either be a struct or all it's properties should be `let's.
Be sure to read and understand #par's answer to this question. He wrote a really good explanation and it would be a shame to waste that knowledge transfer.

Overriding java method with Vector type parameter

I am implementing a ContractNetInitiator from Jade in scala and I need to override this method:
protected void handleAllResponses(java.util.Vector responses,java.util.Vector acceptances)
And implemented it like this:
override def handleAllResponses(responses: Vector[_], acceptances: Vector[_]): Unit = {
var acceptProposal: ACLMessage = null
var bestProposal = Integer.MAX_VALUE
var bestProposer = ""
// Find best proposal and reject all proposal
val e = responses.elements()
while (e.hasMoreElements) {
val response: ACLMessage = e.nextElement().asInstanceOf[ACLMessage]
if (response.getPerformative == ACLMessage.PROPOSE) {
val reply = response.createReply()
reply.setPerformative(ACLMessage.REJECT_PROPOSAL)
acceptances.addElement(reply) // Can't add the reply : "Type mismatch, expected: _$1, actual: ACLMessage"
if (response.getUserDefinedParameter("Value").toInt < bestProposal) {
bestProposal = response.getUserDefinedParameter("Value").toInt
bestProposer = response.getSender.getLocalName
acceptProposal = reply
}
}
}
// Accept proposal
if (acceptProposal != null) {
acceptProposal.setPerformative(ACLMessage.ACCEPT_PROPOSAL)
}
}
But when I try to add a reply to acceptances I get a Type mismatch.
I tried to change "acceptances: Vector[_]" with "acceptances: Vector[ACLMessage]" and "acceptances: Vector[Any]", but it doesn't work since it doesn't correspond with the super class.
Is there a way to add elements to acceptances ?
You'll need to cast it:
acceptances.asInstanceOf[Vector[ACLMessage]].addElement(reply)
Normally it's something to avoid, but in this case it's entirely the library's fault for using raw types and only documenting effective type parameters.

TypeScript generic class that creates instances of its type variable's class?

I'm familiar with generic classes in TypeScript, where a class can be defined with an associated type variable, and then instances with a specific type can manipulate and return values of that type.
Problem: I want a generic class that creates instances of the type variable. For instance:
class Thing {
thingProp: string;
}
class ThingOne extends Thing {
thingOneProp: string;
}
class ThingTwo extends Thing {
thingTwoProp: string;
}
class Maker<T extends Thing> {
make(): T {
return new T();
// ^--- " // <- "error TS2304: Cannot find name 'T'""
}
}
let thingOneMaker = new Maker<ThingOne>();
let thingOne: ThingOne = thingOneMaker.make();
let thingTwoMaker = new Maker<ThingTwo>();
let thingTwo: ThingTwo = thingTwoMaker.make();
let thingError: ThingOne = thingTwoMaker.make();
// ^--- "error TS2322: Type 'ThingTwo' is not assignable to type 'ThingOne'"
This almost seems to work. The compiler generates code, and the error on the last line shows that TypeScript understands what type thingTwoMaker.make() should return.
However, the error on return new T(); shows that TypeScript doesn't understand that I'm trying to make instances of the type variable's class, and the generated JavaScript confirms it:
var Maker = (function () {
function Maker() {
}
Maker.prototype.make = function () {
return new T(); // <- "error TS2304: Cannot find name 'T'"
};
return Maker;
}());
And, not surprisingly, running the generated JavaScript with Node.js produces a ReferenceError: T is not defined error.
How can I make a generic class whose instances can create instances of the type variable class? (Tested using TypeScript 2.0.10.)
Somehow, you need to give the Maker class the constructor of the type you'd like it to make, so that it has a value on which to call new.
I'd say a good option would be to pass the class constructor as an argument to Maker's constructor. That will allow it to construct instances of that class, and it will automatically infer the type that it's building so you don't have to manually annotate the generic type anymore.
So maybe something like this:
class Maker<T extends Thing> {
private ctor: {new(): T};
constructor(ctor: {new(): T}) {
this.ctor = ctor;
}
make(): T {
return new this.ctor();
}
}
Then, you can pass the right class constructor to each kind of Maker, and the type will be automatically inferred:
let thingOneMaker = new Maker(ThingOne);
let thingOne: ThingOne = thingOneMaker.make();
let thingTwoMaker = new Maker(ThingTwo);
let thingTwo: ThingTwo = thingTwoMaker.make();
// Still an error.
let thingError: ThingOne = thingTwoMaker.make();
Playground link.

Declare dynamically added class properties in TypeScript

I want to assign properties to the instance of a class without knowing the property names, values and types of values in TypeScript. Lets assume we have the following example.ts script:
// This could be a server response and could look totally diffent another time...
const someJson:string = '{ "foo": "bar", "bar": "baz" }'
class MyClass {
someProperty:boolean
constructor( json:string ) {
const parsedJson:any = JSON.parse( json )
Object.keys( parsedJson ).forEach(
( key:string ) => {
this[ key ] = parsedJson[ key ]
}
)
this['someProperty'] = true
}
}
const myInstance = new MyClass( someJson )
// Works fine, logs `true`.
console.log( myInstance.someProperty )
// Error: Property 'foo' does not exist on type 'MyClass'.
console.log( myInstance.foo )
// Error: Property 'bar' does not exist on type 'MyClass'.
console.log( myInstance.bar )
How can I make sure that the TypeScript compiler does not complain of the dynamically added properties but instead handle them as "key": value pairs of any type. I still want tsc to make sure that myInstance.someProperty has to be of type boolean but I want to be able to get myInstance.whatever even if it is not defined without running into compiler errors.
I did not find any documentation that makes this clear to me. Maybe because I'm not a native english speaker. So please keep the answers simple.
Edit:
I remember that there was something like the following but I never got that to work:
interface IMyClass {
[name:string]: any
}
The problem is that you're adding the new properties at runtime and the compiler has no way of knowing that.
If you know the property names in advance then you can do this:
type Json = {
foo: string;
bar: string;
}
...
const myInstance = new MyClass(someJson) as MyClass & Json;
console.log(myInstance.foo) // no error
Edit
If you do not know the properties in advance then you can't do this:
console.log(myInstance.foo);
Because then you know that foo is part of the received json, you'll probably have something like:
let key = getKeySomehow();
console.log(myInstance[key]);
And this should work without an error from the compiler, the only problem with that is that the compiler doesn't know the type for the returned value, and it will be any.
So you can do this:
const myInstance = new MyClass(someJson) as MyClass & { [key: string]: string };
let foo = myInstance["foo"]; // type of foo is string
let someProperty = myInstance["someProperty"]; // type of someProperty is boolean
2nd edit
As you do know the props, but not in the class, you can do:
type ExtendedProperties<T> = { [P in keyof T]: T[P] };
function MyClassFactory<T>(json: string): MyClass & ExtendedProperties<T> {
return new MyClass(json) as MyClass & ExtendedProperties<T>;
}
Then you simply use it like so:
type Json = {
foo: string;
bar: string;
};
const myInstance = MyClassFactory<Json>(someJson);
Note that this will work only on typescript 2.1 and above.
If you want to dynamically add class properties via an object upon instantiation, and type information is available for that object, you can very nicely get full type safety in this way (as long as you don't mind using a static factory method):
class Augmentable {
constructor(augment: any = {}) {
Object.assign(this, augment)
}
static create<T extends typeof Augmentable, U>(this: T, augment?: U) {
return new this(augment) as InstanceType<T> & U
}
}
This is using the (fake) this parameter to infer the constructor type of the class. It then constructs the instance, and casts it to a union of the instance type (using the InstanceType utility type) and the inferred type of the props you passed to the method.
(We could have casted directly to Augmentable & U, however this way allows us to extend the class.)
Examples
Augment basic properties:
const hasIdProp = Augmentable.create({ id: 123 })
hasIdProp.id // number
Augment with methods:
const withAddedMethod = Augmentable.create({
sayHello() {
return 'Hello World!'
}
})
withAddedMethod.sayHello() // Properly typed, with signature and return value
Extend and augment, with this access in method augments:
class Bob extends Augmentable {
name = 'Bob'
override = 'Set from class definition'
checkOverrideFromDefinition() {
return this.override
}
}
interface BobAugment {
whatToSay: string
override: string
sayHelloTo(to: string): void
checkOverrideFromAugment(): string
}
const bobAugment: BobAugment = {
whatToSay: 'hello',
override: 'Set from augment'
sayHelloTo(this: Bob & BobAugment, to: string) {
// Let's combine a class parameter, augment parameter, and a function parameter!
return `${this.name} says '${this.whatToSay}' to ${to}!`
},
checkOverrideFromAugment(this: Bob & BobAugment) {
return this.override
}
}
const bob = Bob.create(bobAugment) // Typed as Bob & BobAugment
bob.sayHelloTo('Alice') // "Bob says 'hello' to Alice!"
// Since extended class constructors always run after parent constructors,
// you cannot override a class-set parameter with an augment, no matter
// from where you are checking it.
bob.checkOverrideFromAugment() // "Set from class definition"
bob.checkOverrideFromDefinition() // "Set from class definition"
Limitations
Augmented properties aren't really part of the class, so you can't extend a class with those augments included. This may be a feature for some use cases where the augments are temporary additions that aren't meant to modify the prototype hierarchy
It is also not easy to add non-augment arguments to .create(), however an easy work-around is to simply utilize the augment functionality to accomplish the same thing you would have with an extra argument.
You can add index signature to your class:
class MyClass {
[index: string]: any; //index signature
someProperty:boolean
constructor( json:string ) {
const parsedJson:any = JSON.parse( json )
Object.keys( parsedJson ).forEach(
( key:string ) => {
this[ key ] = parsedJson[ key ]
}
)
this['someProperty'] = true
}
}

Unify generic types in macro

I wanted to use macro to check if a function is returning a particular generic type, say Array, so it is fine if the function is returning Array<Dynamic>, Array<String>, or even generic Array<T>.
So I tried to Context.unify it with Array<Dynamic>. It is fine for Array<String> or Array<Dynamic> but it fails when the type parameter is "generic" because the ComplexType Array<T> won't convert to a Type with Type not found: T (See code below). Are there any possible ways to achieve what I am attempting to do?
package;
#if macro
import haxe.macro.Context;
using haxe.macro.ComplexTypeTools;
#end
#if !macro #:build(Macros.build()) #end
class Main
{
public function test<T>():Array<T>
{
return [];
}
}
class Macros
{
public static function build()
{
#if macro
var fields = Context.getBuildFields();
for(field in fields)
{
switch(field.kind)
{
case FFun(f):
// try to unify Array<String> with Array<Dynamic>
trace(Context.unify((macro:Array<String>).toType(), (macro:Array<Dynamic>).toType()));
// true
// try to unify Array<T> with Array<Dynamic>
trace(Context.unify(f.ret.toType(), (macro:Array<Dynamic>).toType()));
// Type not found: T
default:
}
}
return null;
#end
}
}
UPDATE
So, checking TPath was not the best idea.
Based on the previous assumption about Dynamic being assignable to any type we can replace unconvertable type parameter with the Dynamic (eg Array<T> = Array<Dynamic>) and when try to unify it.
static function toTypeExt(c:ComplexType):Null<haxe.macro.Type>
{
try {
return c.toType();
} catch (error:Error)
{
switch (c)
{
case TPath(p):
//deep copy of the TPath with all type parameters as Dynamic
var paramsCopy = [for (i in 0...p.params.length) TPType(macro:Dynamic)];
var pCopy:TypePath = {
name: p.name,
pack: p.pack,
sub: p.sub,
params: paramsCopy
}
var cCopy = TPath(pCopy);
//convert after
return cCopy.toType();
default:
}
}
return null;
}
Use toTypeExt() in your build macro instead of toType.
trace(Context.unify(toTypeExt(f.ret), (macro:Array<Dynamic>).toType()));
Looks more like a workaround to me, but there is a strange thing about ComplexTypeTools.toType - it will succeed with a class type parameter while failing with method type parameter.
OLD ANSWER
Unification won't work since there is no way of converting ComplexType with the type parameter to Type (in that context). But since you are unifying with Array it is safe to assume that any Array will unify with it (since any type is assignable to Dynamic http://haxe.org/manual/types-dynamic.html).
May be it is not the pritiest solution, but simple TPath check is the way to go here:
case FFun(f):
switch (f.ret) {
case TPath({ name: "Array", pack: [], params: _ }):
trace("Yay!");
default:
}