Difference between "toString" and "as String" in Dart? - dart

Is there any difference between .toString and as String in Dart?

toString() is a method on Object and is therefore available on every object. The method is used to get a string representation of the object:
A string representation of this object.
Some classes have a default textual representation, often paired with a static parse function (like int.parse). These classes will provide the textual representation as their string represetion.
Other classes have no meaningful textual representation that a program will care about. Such classes will typically override toString to provide useful information when inspecting the object, mainly for debugging or logging.
https://api.dart.dev/stable/2.13.4/dart-core/Object/toString.html
as String is a typecast in Dart and is used to tell the analyzer/compiler that whatever it assumes, you are now going to tell it that your object is in fact a String at runtime. You can hereafter use the object like a String.
But the compiler will add a check at runtime and if the object is not compatible with the interface of String, your application will crash because you have lied to the compiler.
It is therefore two entire different things and is used for different purposes. You can e.g. not use as String on an object which is not already a String.
The safest you can do is just call toString() since toString() on String will just return itself.

They are completely different!
.toString() is a method to represent data of a object but as String is a type cast which tries to convert the object itself to a String .
Imagine you have a class named Person
class Person {
String firstName;
String lastName;
Person(
this.firstName,
this.lastName,
);
}
now casting person to a String will lead to an _CastError error since Person is not a String or a subtype of String(inherited classes from String class)
final person = Person('sajad', 'abd');
final personAsString = person as String;
Meanwhile, the method .toString() will represent you object in a String.
final person = Person('sajad', 'abd');
final personToString = person.toString();
print(personToString); // result: Instance of 'Person'
.toString() is defined for every class in dart and you can override it in you custom classes
for example you can override it in Person class to represent firstname and lastname of person
class Person {
String firstName;
String lastName;
Person(
this.firstName,
this.lastName,
);
#override
String toString() => 'Person(firstName: $firstName, lastName: $lastName)';
}
And now the result of
final person = Person('sajad', 'abd');
final personToString = person.toString();
print(personToString);
would be
Person(firstName: sajad, lastName: abd)

Related

Dart equatable with non final variables

I have class with final variable (hash) and some other non-final variables.
Hash is unique value. And objects are stored in Set. Set uses '==' operand to check equality of objects. I want to override "==" and "hashCode" in my class and work with Set array.
To avoid using boilerplate code I want to use Equatable extension. Like this
class User extends Equatable {
final String hash;
String balance;
bool state;
....
#override
List<Object> get props => [hash];
} .... Set<User> users
Is it correct way to use Equatable in my case, 'cause it is uses with immutable classes.
Thanks!
Overriding hashCode to depend on non-final fields is usually not recommended because it can make Sets and Maps and other data structures that depend on hashCode internally inconsistent. Suppose you have such an object and insert it into a Set. Later, you mutate that object by assigning a new value to that field, but the Set would still have a reference to that object with the old hash code. For example, consider:
class Foo {
String s;
Foo(this.s);
#override
bool operator ==(Object other) {
return other is Foo && s == other.s;
}
#override
int get hashCode => s.hashCode;
#override
String toString() => s;
}
void main() {
var foo = Foo('foo');
var someSet = <Foo>{foo};
foo.s = 'bar';
print(someSet.contains(foo)); // Prints: false
someSet.add(foo);
print(someSet.length); // Prints: 2
print(someSet); // Prints: {bar, bar}
}
and now someSet would have two references to the exact same object, which violates its goal of storing unique objects.
A Map would have similar problems.
Since hashCode is tied to operator ==, this consequently also means that you usually shouldn't override operator == to depend on non-final fields.
You can get away with it if you can guarantee that you never mutate your objects while they're being referenced by a Set/Map/etc. or if you can guarantee that whenever you want to add your object to a Set/Map/etc. that you create a copy of your object and add that copy instead.
I'd say your use is correct.
You use Equatable only with the final field, so the equality and hash code should be stable over time, and the hash field is unique, so it can serve as identifier for the object.
If your hash field ends up not unique, you'll have two distinguishable objects that are equal, so ... don't do that.
Not sure how much you gain from using Equatable, though. If you wrote the equality and hash code yourself, it would just be:
bool operator==(Object other) => other is Foo && hash == other.hash;
int get hashCode => hash.hashCode;

is there a way to perform optional casting in Dart?

in Swift or Kotlin I can do something like this
var fullName = myMap["fullName"] as? String
then as a result that fullName data type will be optional String ( String? ).
I need to get optional type after type checking like that
I can't directly perform null coalescing operator to that map, because dart will give weird result. for example like this
// 'data' is Map<String, dynamic>
final fullName = data["fullname"] ?? "John Doe";
final double myNumber = fullName;
as you can see, the IDE will not show an error at all, I expect that fullName will be a String, so it will have an error when I assign a String to myNumber that require double.
If you know in advance that data["fullname"] is a String, then you could do:
final fullName = (data["fullname"] ?? "John Doe") as String;
If data["fullname"] turns out not to be a String at runtime, you'll get a runtime exception from the cast failure. If that's something you need to handle, then you could easily make a trivial helper function that checks if a dynamic value is the desired type first and that returns null if it isn't:
T? tryCast<T>(dynamic object) => object is T ? object : null;
final fullName = tryCast<String>(data["fullname"]) ?? "John Doe";
and now fullName is statically known to be a String, and accidentally assigning it to a double will be a compile-time error.
The safe nullable cast operator known from Kotlin currently doesn't exist in Dart but it soon might.
In your case though, why not simply write
String? fullname = myMap["fullname"];
The nullable cast operator as? in Kotlin yields null if myMap["fullname"] contains anything but a non-null String. As long as you're only dealing with Strings or null, the above works just fine. (And if there's anything but a String or null it crashes, which is probably better than just continue on with null in most situations)

How do I initialize non-nullable members in a constructor body?

I've created my class in Dart this way, but I'm getting the Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'. I would like to know if there's a way to do it in a 'Python' style where this kind of class creation is possible, thank you in advance.
class Lexer {
String _text;
int _pos;
String _current_char;
Lexer(String text) {
this._text = text;
this._pos = -1;
this._current_char = '';
this.advance();
}
void advance() {
this._pos++;
this._current_char = this._pos < this._text.length ? this._text[this._pos] : '';
}
}
class Lexer {
String _text;
int _pos;
String _current_char;
This declares several members with type String. Since they are declared as String and not as String?, these members are non-nullable; they are not allowed to ever be null. (This is part of the new null-safety feature from Dart 2.12.)
Dart initializes objects in two phases. When the constructor's body runs, Dart expects all member variables to already be initialized. Because your members are non-nullable and haven't been initialized to non-null values yet, this is an error. The error message explains what you can do:
Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Use initializer expressions. This means using an initializer list:
Lexer(String text)
: _text = text,
_pos = -1,
_current_char = '' {
advance();
}
Note that if you're initializing members with a construction parameter of the same name, you can use shorthand:
Lexer(this._text)
: _pos = -1,
_current_char = '' {
advance();
}
Adding field initializers. This means initializing members inline in the class declaration.
class Lexer {
String _text = '';
int _pos = -1,
String _current_char = '';
Marking your members as late. This means that you promise that the variables will be initialized before anything attempts to use them.
class Lexer {
late String _text;
late int _pos,
late String _current_char;
Making your members nullable, which allows them to be implicitly null by default:
class Lexer {
String? _text;
int? _pos,
String? _current_char;
However, that will require that all accesses explicitly check that the members aren't null before using them.
You also might want to read: Dart assigning to variable right away or in constructor?

Some mistakes in the language

I'm new in Dart, I want to precise this. I create two classes in Dart, one is 'Person' and the other one is a child of the first one, and it's named 'Employee'.
I create an object of Person. When I change this object to an Instance of class Employee, nothing wrong. But at the time that I'm asking to a parameter that is inside the Employee, I raised an error.
So why Dart allow me to the class of the object, but not allowed me to access to a parameter inside the new class?
The code below :
void main {
var person = Person(name: "Zozor");
print(person.describe());
person = Employee(taxCode: 'AAB');
person.sayName();
print(person.taxCode);
}
class Person {
Person({this.name, this.age, this.height});
String name;
final int age;
final double height;
String describe() => "Hello, I'm ${this.name}. I'm ${this.age} and I'm ${this.height} meter${this.height == 1 ? '':'s'} tall";
void sayName()=> print("Hello, I'm ${this.name}.");
}
class Employee extends Person {
Employee({String name, int age, double height, this.taxCode, this.salary}) : super(name:name, age: age, height: height);
final String taxCode;
final int salary;
}
Variables in Dart must be declared before being used. They have a type and store a reference to the value (see https://www.tutorialspoint.com/dart_programming/dart_programming_variables.htm).
var person = Person(name: "Zozor");
Declares the variable person of type Person (type is derived from the type of class it is initialized to).
When you assign:
person = Employee(taxCode: 'AAB');
The type is unchanged with the assignment (i.e. remains Person), only the reference changes to the result of downcasting the Employee to a Person (the downcasting is done implicitly as described https://news.dartlang.org/2012/05/types-and-casting-in-dart.html).
The above is due to var creating static type variables.
An alternative would be to use dynamic typing as in:
dynamic person = Person(name: "Zozor");
This declares a person variables whose type is dynamic. Now when the assignment is made to Employee:
person = Employee(taxCode: 'AAB');
The type of the person variable is now Employee rather than Person. Furthermore, there is no downcasting of Employee and no error message related to taxCode.
A simple way to stay with static (rather than using dynamic) is to use an explicit recasting of person to Employee:
print((person as Employee).taxCode);
This casts a person to Employee then gets the taxCode.

Writing to mutable property for a struct record is not allowed in F#. Why?

When I have the following code:
[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}
let john = { FirstName = "John"; LastName = "Connor"}
john.FirstName <- "Sarah";
The compiler complains that "A value must be mutable in order to mutate the contents". However when I remove the Struct attribute it works fine. Why is that so ?
This protects you from a gotcha that used to plague the C# world a few years back: structs are passed by value.
Note that the red squiggly (if you're in IDE) appears not under FirstName, but under john. The compiler complains not about changing the value of john.FirstName, but about changing the value of john itself.
With non-structs, there is an important distinction between the reference and the referenced object:
Both the reference and the object itself can be mutable. So that you can either mutate the reference (i.e. make it point to a different object), or you can mutate the object (i.e. change the contents of its fields).
With structs, however, this distinction does not exist, because there is no reference:
This means that when you mutate john.FirstName, you also mutate john itself. They are one and the same.
Therefore, in order to perform this mutation, you need to declare john itself as mutable too:
[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}
let mutable john = { FirstName = "John"; LastName = "Connor"}
john.FirstName <- "Sarah" // <-- works fine now
For further illustration, try this in C#:
struct Person
{
public string FirstName;
public string LastName;
}
class SomeClass
{
public Person Person { get; } = new Person { FirstName = "John", LastName = "Smith" };
}
class Program
{
static void Main( string[] args )
{
var c = new SomeClass();
c.Person.FirstName = "Jack";
}
}
The IDE will helpfully underline c.Person and tell you that you "Cannot modify the return value of 'SomeClass.Person' because it is not a variable".
Why is that? Every time you write c.Person, that is translated into calling the property getter, which is just like another method that returns you a Person. But because Person is passed by value, that returned Person is going to be a different Person every time. The getter cannot return you references to the same object, because there can be no references to a struct. And therefore, any changes you make to this returned value will not be reflected in the original Person that lives inside SomeClass.
Before this helpful compiler error existed, a lot of people would do this:
c.Person.FirstName = "Jack"; // Why the F doesn't it change? Must be compiler bug!
I clearly remember answering this question almost daily. Those were the days! :-)

Resources