class Car {
String model;
String brand;
String _engine;
static int carProduced = 0;
Car(String model, String brand, String engine) {
this._engine = engine;
this.brand = brand;
this.model = model;
}
}
I am getting this error.
Non-nullable instance field '_engine' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
I am not actually sure. As I am initializing that non-nullable field in the default constructor why do I need to use a late modifier here?
String model = "";
String brand = "";
String _engine = "";
Adding initializer expression solved the error.
Does it mean that object fields are created even before the constructor call ??
Dart objects are created in two steps/phases:
Initialization of values.
Execution of constructor body.
So all non-nullable non-late values must have a value before we executes the constructor body. The reason is that inside the constructor body we are allowed to use all values inside the object.
We can therefore in Dart run initialization code before running the constructor body like:
class Car {
String model;
String brand;
String _engine;
static int carProduced = 0;
Car(String model, String brand, String engine)
: this.model = model,
this.brand = brand,
this._engine = engine;
}
But since this is kinda redundant we have the follow shortcut to do the same:
class Car {
String model;
String brand;
String _engine;
static int carProduced = 0;
Car(this.model, this.brand, this._engine);
}
Related
I'm new to Dart, and I was learning about Classes in Dart.
I'm facing a problem understanding how constructors in Dart actually work.
I was used to initialize fields within constructors in the following way with other programming languages, but in Dart I get many warnings and errors with the following way of initializing fields.
class Car {
String brand;
String model;
int year;
Car(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
}
I think that the problem is concerned with the null safety in Dart, but I don't want to use the ? operator or the keyword late at the time of the fields declaration.
So, could anyone tell me how I could initialize the fields properly using constructors in Dart?
Thanks
The problem is that the constructor body is essentially just a function. Which means you can do anything that a function can normally do. Including this:
class Car {
String brand;
String model;
int year;
void printCarDetails() => print("This car is from $year");
Car(String brand, String model, int year) {
printCarDetails(); // <-- what should this print?
this.brand = brand;
this.model = model;
this.year = year; // <-- if [year] is only initialized here?
}
}
The problem is that you can use fields before you've initialized them (remember that int isn't null by default -- int? is). Dart needs a section before the constructor body where you can guarantee you've initialized everything, and then you can run your constructor body as normal.
That section is called an initializer list:
class Car {
String brand;
String model;
int year;
Car(String brand, String model, int year) :
brand = brand,
model = model,
year = year
{
printCarDetails();
}
void printCarDetails() => print("This car is from $year");
}
So everything after the : is just initializing fields, and then everything in the { } is the constructor body, which can then access those fields.
But the initializer list is ugly to look at. Thankfully, Dart provides a shorthand called initializing formal parameters:
class Car {
String brand;
String model;
int year;
Car(this.brand, this.model, this.year) {
// brand, model, and year are all initialized now
printCarDetails();
}
void printCarDetails() => print("This car is from $year");
}
This is the idiomatic way of initializing fields, and you should try to stick to this pattern when you can.
As a side note, you may run into this problem when using the constructor of a super class:
class Vehicle {
String brand;
Vehicle(this.brand);
}
class Car extends Vehicle {
String model;
// Passes [model] to the initializer list, and [numWheels] to Vehicle
Car(this.model, super.numWheels);
}
This feature is called super parameters, and it is relatively new.
Instance fields must either have an initializer at the declaration, use an initializing formal, or be initialized in the constructor's initialization list.
Here are the examples:
// initializer at the declaration
class Car {
String brand = 'Brand';
String model = 'Model';
int year = 2023;
Car();
}
// initializing formal
class Car {
String brand;
String model;
int year;
Car(this.brand, this.model, this.year);
}
// constructor's initialization list
class Car {
String brand;
String model;
int year;
Car(String brand, String model, int year)
: this.brand = brand,
this.model = model,
this.year = year;
}
For more information and better understanding why dart works in that way please read the official documentation.
When I compile the following code:
class Student {
int id;
Student() {
this.id = 12345;
}
}
void main() {
var student1 = new Student();
}
I get the following error:
Error: Field 'id' should be initialized because its type 'int' doesn't
allow null.
But why do I get this error? I did initialize id in the constructor!
In Dart, the creation of objects are split into two phases:
Initialization of all values.
Execution of constructor body.
So when you are running code inside the constructor body (between the {...} in the constructor definition) then all class defined variables must have been provided a default value that is valid for the type of variable.
In your case, the variable is typed int but are not provided a default value. In Dart, all variable will by default be set to null in case of no other value provided. But since int is a non-nullable type it does not allow null to be a value and the compiler are therefore giving you the error.
The solution are to provide a value before the constructor is running. You can do that like this:
class Student {
int id;
Student() : id = 12345;
}
Or:
class Student {
int id = 12345;
Student(); // The constructor can in theory just be removed here
}
In case you cannot define a value as part of the initialization phase, you can (but should be prevented if possible) mark the variable as late which makes it so you promise, the Dart compiler, that you are going to provide a value for the variable before the first time you are trying to read from that variable:
class Student {
late int id;
Student() {
this.id = 12345;
}
}
In case you are trying to read from id before it have been provided a value, the program will crash with a LateInitializationError at runtime.
And at last, you can set the type to be a nullable type, like int?, to allow the variable to have a default value of null. But doing so will require you to check for null when you are trying to do something with the value in a context where null is not allowed:
class Student {
int? id;
Student() {
this.id = 12345;
}
}
I encountered a problem when writing the constructor in dart. I have a class with two final variables, initialize them in the constructor, the following is wrong, because the final variable has no setter method:
class Person{
final String name;
final int age;
// Error
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
but this is correct, why
class Person{
final String name;
final int age;
// Correct
Person(String name, int age): this.name = name, this.age = age;
}
When the constructor body is executed, final fields are already sealed.
The constructor initializer list is executed before the constructor initializers of the super classes.
The constructor bodies are executed afterwards. Constructor body allows arbitrary code to be executed like reading from fields. This is why at this point the initialization of final fields has to be completed already, otherwise it would be possible to read from a not yet initialized final field.
The constructor initializer list is the supported window where final fields can be initialized. It does not allow reading from this (explicit or implicit) and is therefore safe.
This is just a measure to ensure object initialization always happens in predictable manner.
Is it possible to automatically initialize fields from a parent class in the constructor?
I get the syntax error:
Could not match parameter initializer 'this.name' with any field
class Type {
String name;
}
class Language extends Type {
String id;
Language(this.name) {
While your case is common, at this time the dart language spec specifically says:
Executing an initializing formal this.id causes the field id of the immediately surrounding class to be assigned the value of the corresponding actual parameter.
This essentially tells us that this.variable notation, in the constructor arguments, will only work on variables in the immediate class and not any parent classes. There are a couple of solutions available: The first is to assign it within the constructor's body:
class Type {
String name;
}
class Language extends Type {
String id;
Language(name) {
this.name = name;
}
}
Alternatively, if we can change the parent class to have a constructor which will initialize the variable then we can use the initializer list in the child class:
class Type {
String name;
Type();
Type.withName(this.name);
}
class Language extends Type {
String id;
Language(name) : super.withName(name);
}
This is assuming there's some reason that the default constructor for Type doesn't automatically initialize name so we created the 2nd named constructor instead.
I get the following error when using a primitive attribute in my grails domain object:
Null value was assigned to a property of primitive type setter of MyDomain.myAttribute
org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of MyDomain.myAttribute
at grails.orm.HibernateCriteriaBuilder.invokeMethod(HibernateCriteriaBuilder.java:1077)
According to this SO thread, the solution is to use the non-primitive wrapper types; e.g., Integer instead of int.
A null value cannot be assigned to a primitive type, like int, long, boolean, etc. If the database column that corresponds to the field in your object can be null, then your field should be a wrapper class, like Integer, Long, Boolean, etc.
The danger is that your code will run fine if there are no nulls in the DB, but will fail once nulls are inserted.
And you can always return the primitive type from the getter. Ex:
private Integer num;
public void setNum(Integer i) {
this.num = i;
}
public int getNum() {
return this.num;
}
But in most cases you will want to return the wrapper class.
So either set your DB column to not allow nulls, or use a wrapper class.
A primitive type cannot be null. So the solution is replace primitive type with primitive wrapper class in your tableName.java file.
Such as:
#Column(nullable=true, name="client_os_id")
private Integer client_os_id;
public int getClient_os_id() {
return client_os_id;
}
public void setClient_os_id(int clientOsId) {
client_os_id = clientOsId;
}
reference http://en.wikipedia.org/wiki/Primitive_wrapper_class to find wrapper class of a primivite type.
I'll try to make you understand with the help of an example. Suppose you had a relational table (STUDENT) with two columns and ID(int) and NAME(String). Now as ORM you would've made an entity class somewhat like as follows:-
package com.kashyap.default;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* #author vaibhav.kashyap
*
*/
#Entity
#Table(name = "STUDENT")
public class Student implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1354919370115428781L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "NAME")
private String name;
public Student(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Lets assume table already had entries. Now if somebody asks you add another column of "AGE" (int)
ALTER TABLE STUDENT ADD AGE int NULL
You'll have to set default values as NULL to add another column in a pre-filled table. This makes you add another field in the class. Now the question arises whether you'll be using a primitive data type or non primitive wrapper data type for declaring the field.
#Column(name = "AGE")
private int age;
or
#Column(name = "AGE")
private INTEGER age;
you'll have to declare the field as non primitive wrapper data type because the container will try to map the table with the entity. Hence it wouldn't able to map NULL values (default) if you won't declare field as wrapper & would eventually throw "Null value was assigned to a property of primitive type setter" Exception.
use Integer as the type and provide setter/getter accordingly..
private Integer num;
public Integer getNum()...
public void setNum(Integer num)...
#Column(name ="LEAD_ID")
private int leadId;
Change to
#Column(name ="LEAD_ID")
private Integer leadId;
There are two way
Make sure that db column is not allowed null
User Wrapper classes for the primitive type variable like private int var; can be initialized as private Integer var;
Do not use primitives in your Entity classes, use instead their respective wrappers. That will fix this problem.
Out of your Entity classes you can use the != null validation for the rest of your code flow.
Either fully avoid null in DB via NOT NULL and in Hibernate entity via #Column(nullable = false) accordingly or use Long wrapper instead of you long primitives.
A primitive is not an Object, therefore u can't assign null to it.
#Dinh Nhat, your setter method looks wrong because you put a primitive type there again and it should be:
public void setClient_os_id(Integer clientOsId) {
client_os_id = clientOsId;
}
Change the parameter type from primitive to Object and put a null check in the setter. See example below
public void setPhoneNumber(Long phoneNumber) {
if (phoneNumber != null)
this.phoneNumber = phoneNumber;
else
this.extension = 0l;
}
Make sure your database myAttribute field contains null instead of zero.