I've been struggling with trying to figure out the problem and fixing the error when I tried to pass HashMap into a constructor. My scenario is:
I've a Student class:
public class Student {
String name;
String major;
String level;
public Student (String name, String major, String level) {
this.name = name;
this.major = major;
this.level = level;
}
}
I've another class, called TA_Manager that is a subclass of Student. This TA_Manager class uses HashMap to collect the students (who are TA) from the Student class:
import java.util.HashMap;
public class TA_Manager extends Student {
HashMap<String, Student> TA;
public TA_Manager(HashMap<String, Student> TA) {
this.TA = TA;
}
}
In the main class, I've created three student objects and I put two of the students into the HashMap (they are TAs). Then I create a TA_Manager object and pass the HashMap into the TA_Manager class:
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
Student s1 = new Student("A", "CS", "Junior");
Student s2 = new Student("B", "IS", "Senior");
Student s3 = new Student("C", "CE", "Senior");
HashMap<String, Student> TA = new HashMap<String, Student>();
TA.put("TA1", s1);
TA.put("TA2", s2);
TA_Manager tamgr = new TA_Manager (TA);
}
}
When I run the main class, it returns error:
TA_Manager.java:6: error: constructor Student in class Student cannot be applied to given types;
public TA_Manager(HashMap<String, Student> TA) {
^
required: String,String,String
found: no arguments
I actually have searched this HashMap problem and I followed the solution given on how to pass the HashMap into the constructor:
Pass a HashMap as parameter in Java
and also from this link on how to pass a class as hashmap value:
Can HashMap contain custom class for key/value?
But I still get the error message. I don't know how to fix this error. Can anyone bring some light into this. Really appreciated.
The error is caused because java is trying to call the no-arg constructor of your Student class, but you only have a three argument public constructor.
The simplest solution is to create an empty public constructor for your student.
public Student(){
//do nothing and leave values as null.
}
This is not a very practical solution. The problem is a bit conceptual. Your TA class is a Student, but you don't give it a name major or level.
The next way to manage this would be to call the current constructor with some values.
public TA_Manager(HashMap<String, Student> TA) {
super( null, null, null);
this.TA = TA;
}
Now java knows to use the public constructor instead of the no-arg one. I left the values as null because I don't know what default values you would have. This is practical when there are useful default values that you wouldn't need to include during construction.
Personally, I would expect the TA to be a full student AND have a hashmap.
public TA_Manager (String name, String major, String level) {
super(name, major, level);
this.TA = new HashMap<>();
}
In this case you would create the manager, then add all of the students afterwards. It has the advantage that your TA_Manager is a fully formed student though.
I am trying to figure out if I can work with Kotlin and Spark,
and use the former's data classes instead of Scala's case classes.
I have the following data class:
data class Transaction(var context: String = "", var epoch: Long = -1L, var items: HashSet<String> = HashSet()) :
Serializable {
companion object {
#JvmStatic
private val serialVersionUID = 1L
}
}
And the relevant part of the main routine looks like this:
val transactionEncoder = Encoders.bean(Transaction::class.java)
val transactions = inputDataset
.groupByKey(KeyExtractor(), KeyExtractor.getKeyEncoder())
.mapGroups(TransactionCreator(), transactionEncoder)
.collectAsList()
transactions.forEach { println("collected Transaction=$it") }
With TransactionCreator defined as:
class TransactionCreator : MapGroupsFunction<Tuple2<String, Timestamp>, Row, Transaction> {
companion object {
#JvmStatic
private val serialVersionUID = 1L
}
override fun call(key: Tuple2<String, Timestamp>, values: MutableIterator<Row>): Transaction {
val seq = generateSequence { if (values.hasNext()) values.next().getString(2) else null }
val items = seq.toCollection(HashSet())
return Transaction(key._1, key._2.time, items).also { println("inside call Transaction=$it") }
}
}
However, I think I'm running into some sort of serialization problem,
because the set ends up empty after collection.
I see the following output:
inside call Transaction=Transaction(context=context1, epoch=1000, items=[c])
inside call Transaction=Transaction(context=context1, epoch=0, items=[a, b])
collected Transaction=Transaction(context=context1, epoch=0, items=[])
collected Transaction=Transaction(context=context1, epoch=1000, items=[])
I've tried a custom KryoRegistrator to see if it was a problem with Kotlin's HashSet:
class MyRegistrator : KryoRegistrator {
override fun registerClasses(kryo: Kryo) {
kryo.register(HashSet::class.java, JavaSerializer()) // kotlin's HashSet
}
}
But it doesn't seem to help.
Any other ideas?
Full code here.
It does seem to be a serialization issue.
The documentation of Encoders.bean states (Spark v2.4.0):
collection types: only array and java.util.List currently, map support is in progress
Porting the Transaction data class to Java and changing items to a java.util.List seems to help.
I am looking for a flexible way of "modifying" (copying with some values changed) immutable objects in groovy. There is a copyWith method but it allows you only to replace some properties of the object. It doesn't seem to be convenient enough.
Let's say we have a set of classes representing a domain design of some system:
#Immutable(copyWith = true)
class Delivery {
String id
Person recipient
List<Item> items
}
#Immutable(copyWith = true)
class Person {
String name
Address address
}
#Immutable(copyWith = true)
class Address {
String street
String postalCode
}
Let's assume I need to change street of delivery recipient. In case of regular mutable object it is just fine to perform:
delivery.recipient.address.street = newStreet
or (perhaps useful in some cases):
delivery.with {recipient.address.street = newStreet}
When it comes to do the same with immutable objects the best way according to my knowledge would be:
def recipient = delivery.recipient
def address = recipient.address
delivery.copyWith(recipient:
recipient.copyWith(address:
address.copyWith(street: newStreet)))
It is actually needed for Spock integration test code so readability and expressiveness matters. The version above cannot be used "on the fly" so in order to avoid creating tons of helper methods, I have implemented my own copyOn (since copyWith was taken) method for that which makes it possible to write:
def deliveryWithNewStreet = delivery.copyOn { it.recipient.address.street = newStreet }
I wonder however if there is an ultimate solution for that, present in groovy or provided by some external library. Thanks
For the sake of completeness I provide my implementation of copyOn method. It goes as follows:
class CopyingDelegate {
static <T> T copyOn(T source, Closure closure) {
def copyingProxy = new CopyingProxy(source)
closure.call(copyingProxy)
return (T) copyingProxy.result
}
}
class CopyingProxy {
private Object nextToCopy
private Object result
private Closure copyingClosure
private final Closure simplyCopy = { instance, property, value -> instance.copyWith(createMap(property, value)) }
private final def createMap = { property, value -> def map = [:]; map.put(property, value); map }
CopyingProxy(Object nextToCopy) {
this.nextToCopy = nextToCopy
copyingClosure = simplyCopy
}
def propertyMissing(String propertyName) {
def partialCopy = copyingClosure.curry(nextToCopy, propertyName)
copyingClosure = { object, property, value ->
partialCopy(object.copyWith(createMap(property, value)))
}
nextToCopy = nextToCopy.getProperties()[propertyName]
return this
}
void setProperty(String property, Object value) {
result = copyingClosure.call(nextToCopy, property, value)
reset()
}
private void reset() {
nextToCopy = result
copyingClosure = simplyCopy
}
}
It is then just a matter of adding the delegated method in Delivery class:
Delivery copyOn(Closure closure) {
CopyingDelegate.copyOn(this, closure)
}
High level explanation:
First of all it is required to notice that the code of: delivery.recipient.address.street = newStreet is interpreted as:
Accessing recipient property of delivery object
Accessing address of what was the result of the above
Assigning property street with the value of newStreet
Of course the class CopyingProxy does not have any of those properties, so propertyMissing method will be involved.
So as you can see it is a chain of propertyMissing method invocations terminated by running setProperty.
Base case
In order to implement the desired functionality we maintain two fields: nextToCopy (which is delivery at the beginning) and copyingClosure (which is initialised as a simple copy using copyWith method provided by #Immutable(copyWith = true) transformation).
At this point if we had a simple code like delivery.copyOn { it.id = '123' } then it would be evaluated as delivery.copyWith [id:'123'] according to simplyCopy and setProperty implementations.
Recursive step
Let's now see how would it work with one more level of copying: delivery.copyOn { it.recipient.name = 'newName' }.
First of all we will set initial values of nextToCopy and copyingClosure while creating CopyingProxy object same way as in the previous example.
Let's now analyse what would happen during first propertyMissing(String propertyName) call. So we would capture current nextToCopy (delivery object), copyingClosure (simple copying based on copyWith) and propertyName (recipient) in a curried function - partialCopy.
Then this copying will be incorporated in a closure
{ object, property, value -> partialCopy(object.copyWith(createMap(property, value))) }
which becomes our new copyingClosure. In the next step this copyingClojure is invoked in the way described in Base Case part.
Conclusion
We have then executed: delivery.recipient.copyWith [name:'newName']. And then the partialCopy applied to the result of that giving us delivery.copyWith[recipient:delivery.recipient.copyWith(name:'newName')]
So it's basically a tree of copyWith method invocations.
On top of that you can see some fiddling with result field and reset function. It was required to support more than one assignments in one closure:
delivery.copyOn {
it.recipient.address.street = newStreet
it.id = 'newId'
}
Is there a Language supported way to make a full (deep) copy of an Object in Dart?
If multiple options exist, what are their differences?
Darts built-in collections use a named constructor called "from" to accomplish this. See this post: Clone a List, Map or Set in Dart
Map mapA = {
'foo': 'bar'
};
Map mapB = new Map.from(mapA);
No as far as open issues seems to suggest:
https://github.com/dart-lang/sdk/issues/3367
And specifically:
... Objects have identity, and you can only pass around references to them. There is no implicit copying.
Late to the party, but I recently faced this problem and had to do something along the lines of :-
class RandomObject {
RandomObject(this.x, this.y);
RandomObject.clone(RandomObject randomObject): this(randomObject.x, randomObject.y);
int x;
int y;
}
Then, you can just call copy with the original, like so:
final RandomObject original = RandomObject(1, 2);
final RandomObject copy = RandomObject.clone(original);
I guess for not-too-complex objects, you could use the convert library:
import 'dart:convert';
and then use the JSON encode/decode functionality
Map clonedObject = JSON.decode(JSON.encode(object));
If you're using a custom class as a value in the object to clone, the class either needs to implement a toJson() method or you have to provide a toEncodable function for the JSON.encode method and a reviver method for the decode call.
Unfortunately no language support. What I did is to create an abstract class called Copyable which I can implement in the classes I want to be able to copy:
abstract class Copyable<T> {
T copy();
T copyWith();
}
I can then use this as follows, e.g. for a Location object:
class Location implements Copyable<Location> {
Location({
required this.longitude,
required this.latitude,
required this.timestamp,
});
final double longitude;
final double latitude;
final DateTime timestamp;
#override
Location copy() => Location(
longitude: longitude,
latitude: latitude,
timestamp: timestamp,
);
#override
Location copyWith({
double? longitude,
double? latitude,
DateTime? timestamp,
}) =>
Location(
longitude: longitude ?? this.longitude,
latitude: latitude ?? this.latitude,
timestamp: timestamp ?? this.timestamp,
);
}
To copy an object without reference, the solution I found was similar to the one posted here, however if the object contains MAP or LIST you have to do it this way:
class Item {
int id;
String nome;
String email;
bool logado;
Map mapa;
List lista;
Item({this.id, this.nome, this.email, this.logado, this.mapa, this.lista});
Item copyWith({ int id, String nome, String email, bool logado, Map mapa, List lista }) {
return Item(
id: id ?? this.id,
nome: nome ?? this.nome,
email: email ?? this.email,
logado: logado ?? this.logado,
mapa: mapa ?? Map.from(this.mapa ?? {}),
lista: lista ?? List.from(this.lista ?? []),
);
}
}
Item item1 = Item(
id: 1,
nome: 'João Silva',
email: 'joaosilva#gmail.com',
logado: true,
mapa: {
'chave1': 'valor1',
'chave2': 'valor2',
},
lista: ['1', '2'],
);
// -----------------
// copy and change data
Item item2 = item1.copyWith(
id: 2,
nome: 'Pedro de Nobrega',
lista: ['4', '5', '6', '7', '8']
);
// -----------------
// copy and not change data
Item item3 = item1.copyWith();
// -----------------
// copy and change a specific key of Map or List
Item item4 = item1.copyWith();
item4.mapa['chave2'] = 'valor2New';
See an example on dartpad
https://dartpad.dev/f114ef18700a41a3aa04a4837c13c70e
With reference to #Phill Wiggins's answer, here is an example with .from constructor and named parameters:
class SomeObject{
String parameter1;
String parameter2;
// Normal Constructor
SomeObject({
this.parameter1,
this.parameter2,
});
// .from Constructor for copying
factory SomeObject.from(SomeObject objectA){
return SomeObject(
parameter1: objectA.parameter1,
parameter2: objectA.parameter2,
);
}
}
Then, do this where you want to copy:
SomeObject a = SomeObject(parameter1: "param1", parameter2: "param2");
SomeObject copyOfA = SomeObject.from(a);
Let's say you a have class
Class DailyInfo
{
String xxx;
}
Make a new clone of the class object dailyInfo by
DailyInfo newDailyInfo = new DailyInfo.fromJson(dailyInfo.toJson());
For this to work your class must have implemented
factory DailyInfo.fromJson(Map<String, dynamic> json) => _$DailyInfoFromJson(json);
Map<String, dynamic> toJson() => _$DailyInfoToJson(this);
which can be done by making class serializable using
#JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false)
Class DailyInfo{
String xxx;
}
It only works for object types that can be represented by JSON.
ClassName newObj = ClassName.fromMap(obj.toMap());
or
ClassName newObj = ClassName.fromJson(obj.toJson());
Trying using a Copyable interface provided by Dart.
there is an easier way for this issue
just use ... operator
for example, clone a Map
Map p = {'name' : 'parsa','age' : 27};
Map n = {...p};
also, you can do this for class properties.
in my case, I was needed to clone a listed property of a class.
So:
class P1 {
List<String> names = [some data];
}
/// codes
P1 p = P1();
List<String> clonedList = [...p.names]
// now clonedList is an unreferenced type
There is no built-in way of deep cloning an object - you have to provide the method for it yourself.
I often have a need to encode/decode my classes from JSON, so I usually provide MyClass fromMap(Map) and Map<String, dynamic> toJson() methods. These can be used to create a deep clone by first encoding the object to JSON and then decoding it back.
However, for performance reasons, I usually implement a separate clone method instead. It's a few minutes work, but I find that it is often time well spent.
In the example below, cloneSlow uses the JSON-technique, and cloneFast uses the explicitly implemented clone method. The printouts prove that the clone is really a deep clone, and not just a copy of the reference to a.
import 'dart:convert';
class A{
String a;
A(this.a);
factory A.fromMap(Map map){
return A(
map['a']
);
}
Map<String, dynamic> toJson(){
return {
'a': a
};
}
A cloneSlow(){
return A.fromMap(jsonDecode(jsonEncode(this)));
}
A cloneFast(){
return A(
a
);
}
#override
String toString() => 'A(a: $a)';
}
void main() {
A a = A('a');
A b = a.cloneFast();
b.a = 'b';
print('a: $a b: $b');
}
There's no API for cloning/deep-copying built into Dart.
We have to write clone() methods ourselves & (for better or worse) the Dart authors want it that way.
Deep copy Object /w List
If the Object we're cloning has a List of Objects as a field, we need to List.generate that field and those Objects need their own clone method.
Example of cloning method (copyWith()) on an Order class with a List field of objects (and those nested objects also have a copyWith()):
Order copyWith({
int? id,
Customer? customer,
List<OrderItem>? items,
}) {
return Order(
id: id ?? this.id,
customer: customer ?? this.customer,
//items: items ?? this.items, // this will NOT work, it references
items: items ?? List.generate(this.items.length, (i) => this.items[i].copyWith()),
);
}
Gunter mentions this here.
Note, we cannot use List.from(items) nor [...items]. These both only make shallow copies.
Dart does not share Memory within multiple threads (isolate), so...
extension Clone<T> on T {
/// in Flutter
Future<T> clone() => compute<T, T>((e) => e, this);
/// in Dart
Future<T> clone() async {
final receive = ReceivePort();
receive.sendPort.send(this);
return receive.first.then((e) => e as T).whenComplete(receive.close);
}
}
An example of Deep copy in dart.
void main() {
Person person1 = Person(
id: 1001,
firstName: 'John',
lastName: 'Doe',
email: 'john.doe#email.com',
alive: true);
Person person2 = Person(
id: person1.id,
firstName: person1.firstName,
lastName: person1.lastName,
email: person1.email,
alive: person1.alive);
print('Object: person1');
print('id : ${person1.id}');
print('fName : ${person1.firstName}');
print('lName : ${person1.lastName}');
print('email : ${person1.email}');
print('alive : ${person1.alive}');
print('=hashCode=: ${person1.hashCode}');
print('Object: person2');
print('id : ${person2.id}');
print('fName : ${person2.firstName}');
print('lName : ${person2.lastName}');
print('email : ${person2.email}');
print('alive : ${person2.alive}');
print('=hashCode=: ${person2.hashCode}');
}
class Person {
int id;
String firstName;
String lastName;
String email;
bool alive;
Person({this.id, this.firstName, this.lastName, this.email, this.alive});
}
And the output below.
id : 1001
fName : John
lName : Doe
email : john.doe#email.com
alive : true
=hashCode=: 515186678
Object: person2
id : 1001
fName : John
lName : Doe
email : john.doe#email.com
alive : true
=hashCode=: 686393765
// Hope this work
void main() {
List newList = [{"top": 179.399, "left": 384.5, "bottom": 362.6, "right": 1534.5}, {"top": 384.4, "left": 656.5, "bottom": 574.6, "right": 1264.5}];
List tempList = cloneMyList(newList);
tempList[0]["top"] = 100;
newList[1]["left"] = 300;
print(newList);
print(tempList);
}
List cloneMyList(List originalList) {
List clonedList = new List();
for(Map data in originalList) {
clonedList.add(Map.from(data));
}
return clonedList;
}
This works for me.
Use the fromJson and toJson from your Object's Class on JSON serializing
var copy = ObjectClass.fromJson(OrigObject.toJson());
make a helper class:
class DeepCopy {
static clone(obj) {
var tempObj = {};
for (var key in obj.keys) {
tempObj[key] = obj[key];
}
return tempObj;
}
}
and copy what you want:
List cloneList = [];
if (existList.length > 0) {
for (var element in existList) {
cloneList.add(DeepCopy.clone(element));
}
}
Let's say, you want to deep copy an object Person which has an attribute that is a list of other objects Skills. By convention, we use the copyWith method with optional parameters for deep copy, but you can name it anything you want.
You can do something like this
class Skills {
final String name;
Skills({required this.name});
Skills copyWith({
String? name,
}) {
return Skills(
name: name ?? this.name,
);
}
}
class Person {
final List<Skills> skills;
const Person({required this.skills});
Person copyWith({
List<Skills>? skills,
}) =>
Person(skills: skills ?? this.skills.map((e) => e.copyWith()).toList());
}
Keep in mind that using only this.skills will only copy the reference of the list. So original object and the copied object will point to the same list of skills.
Person copyWith({
List<Skills>? skills,
}) =>
Person(skills: skills ?? this.skills);
If your list is primitive type you can do it like this. Primitive types are automatically copied so you can use this shorter syntax.
class Person {
final List<int> names;
const Person({required this.names});
Person copyWith({
List<int>? names,
}) =>
Person(names: names ?? []...addAll(names));
}
The accepted answer doesn't provide an answer, and the highest-rated answer 'doesn't work' for more complex Map types.
It also doesn't make a deep copy, it makes a shallow copy which seems to be how most people land on this page. My solution also makes a shallow copy.
JSON-cloning, which a few people suggest, just seems like gross overhead for a shallow-clone.
I had this basically
List <Map<String, dynamic>> source = [{'sampledata', []}];
List <Map<String, dynamic>> destination = [];
This worked, but of course, it's not a clone, it's just a reference, but it proved in my real code that the data types of source and destination were compatible (identical in my case, and this case).
destination[0] = source[0];
This did not work
destination[0] = Map.from(source[0]);
This is the easy solution
destionation[0] = Map<String, dynamic>.from(source[0]);