Currently I'm experimenting and learning the Dart language.
I'm creating an abstract class with two abstract methods called IAnimal like this:
abstract class IAnimal
{
String Walk(int distance);
String Eat(String food);
}
Next I create a Dog class that should implement the 2 methods.
class Dog implements IAnimal
{
Dog(String name) {
this._name = name;
}
String _name;
}
But the dart analyser does complain about the 2 missing methods, is this intended behaviour or not supported?
This should indeed give a warning, the Dart Analyzer from the command line does the trick. This was an issue with WebStorm and Sublime Text not triggering the Dart Analyzer.
Here it shows a warning DartPad maybe the analyzer doesn't run or isn't finished yet.
Related
I am developing external library. Assume I have different implementations of logger class, I create abstract class ILogger which those classes can then implement.
I also have various implementations of log objects that I want to adhere to ILog abstract class so I expose it as well.
The problem is when my ILogger uses ILog as argument to one of its methods. I would assume that any of my implemented logger classes (that implement ILogger) would accept any arguments that are log classes (which implement ILog).
See my constrained example below:
abstract class ILog {
const LogImpl({required this.id});
final String id;
}
class Log implements ILog {
const Log({required this.id});
final String id;
}
abstract class ILogger {
void writeLog({
required LogImpl log,
required bool persist,
});
}
class Logger implements ILogger {
void writeLog({
required Log log,
required bool persist,
}) {
print('writing $log with persistence? $persist');
}
}
void main() {
final logger = Logger();
final log = Log(id: 'abcd-1234');
logger.writeLog(log: log, persist: true)
}
For this I get error:
Error: The parameter 'log' of the method 'Logger.writeLog' has type 'Log', which does not match the corresponding type, 'ILog', in the overridden method, 'ILogger.writeLog'.
Is there a way to solve this without resorting to applying generics?
The reason why my log object ILog needs to be abstract class instead of regular class that is extended is technical. One of my serialization libraries uses source code annotation which means that this annotation cannot be part of the library (because the annotation might be different for different applications).
The program doesn't compile because it's not sound.
Dart, and object oriented programming in general, is based around subtype substitutability. If your code works with an instance of a type, it works with an instance of a subtype too - the subtype can substitute for the supertype.
The Logger's writeLog method is not a valid override of the ILogger's writeLog method. The latter accepts an ILog as argument, and for the subtype to be able to substitute for that, it needs to accept an ILog too. However, it only accepts a Log, which is a subtype, and not any implementation of ILog.
One alternative is to admit that what you are writing is unsound, and tell the compiler to accept it anyway:
class Logger implements ILogger {
void writeLog({
required covariant Log log,
required bool persist,
}) {
print('writing $log with persistence? $persist');
}
}
Here the covariant tells the compiler that you know that Log does not satisfy the superclass parameter type of ILog, but it's OK. You know what you're doing, and no-one will ever call this function with something which isn't a proper Log. (And if they do anyway, it'll throw an error).
The other alternative, which is what I'd probably recommend, is to parameterize your classes on the Log it uses:
abstract class ILogger<L extends ILog> {
void writeLog({
required L log,
required bool persist,
});
}
class Logger implements ILogger<Log> {
void writeLog({
required Log log,
required bool persist,
}) {
print('writing $log with persistence? $persist');
}
}
With that, the Logger doesn't have to substitute for all ILoggers, only for ILogger<Log>, and it can do that soundly.
(Well, as soundly as the inherently unsound covariant generics allow, but the program will compile, and throw if you ever pass something which isn't a Log instance.)
In both cases the compiler will know that the argument to a Logger must be a Log. In both cases, you can fool the program by casting the Logger to the supertype ILogger/ILogger<ILog>, and then pass in an ILog to writeLog, but it takes at least a little effort to circumvent the type system.
I have Android development background and I'm learning Flutter.
In Android it's a common practice to use Kotlin sealed classes to return a state from ViewModel e.g.
sealed class MyState {
data class Success(val data: List<MyObject>) : MyState()
data class Error(val error: String) : MyState()
}
I want to use similar pattern in Flutter and return a State object from the BLOC class. What is the best way to achieve the same in Flutter?
Such use case would be done using named factory constructors.
It requires a lot more code, but the behavior is the same.
class MyState {
MyState._();
factory MyState.success(String foo) = MySuccessState;
factory MyState.error(String foo) = MyErrorState;
}
class MyErrorState extends MyState {
MyErrorState(this.msg): super._();
final String msg;
}
class MySuccessState extends MyState {
MySuccessState(this.value): super._();
final String value;
}
Rémi Rousselet's answer is somehow correct but as sindrenm mentioned:
Unfortunately, this isn't the same thing. Kotlin sealed classes guarantee that there are no other implementations of the given class outside of the file they're defined in. That means you can exhaust when statements (switch in Dart) by just providing all possible alternatives as cases, not having to think about potential sub-classes elsewhere
While there is an active discussion about this feature on dart language: Algebraic Data Types, but there is some libraries that can help you implement this behavior. You can use this libraries:
Sealed Unions
Super Enum
Sealed Class
And if you are using BLoC library you can use this lib:
Sealed Flutter Bloc
I hope that dart language add this feature ASAP
Soon Dart is going to support sealed classes.
GitHub Code: source
sealed class Either {}
class Left extends Either {}
class Right extends Either {}
You can now test the sealed class
test(Either either) {
switch (either) {
case Left(): print('Left');
case Right(): print('Right');
}
}
Here is the package for the Sealed Classes/Unions in Flutter
Freezed
This Package provided the features to deal with Data Classes, Sealed Class in Dart/Flutter
Here is the link which explains the beast use of freezed package in Flutter
Use of Freezed Package in Flutter/Dart
I want to generate an interface with non-default methods. For this purpose I'm using the JvmTypesBuilder.
The code
meth.toMethod(meth.name, meth.returnType)[]
generates for example
public default int meth();
Trying it with
meth.toMethod(meth.name, meth.returnType)[
it.^default = false
]
doesn't change anything.
Setting it abstract works
meth.toMethod(meth.name, meth.returnType)[
it.abstract = true
]
but then I get a method like
public abstract int meth();
what I don't want either.
Is there any way using JvmTypesBuilder and generate a method without default or abstract keywords?
public int meth();
I'm using Eclipse 4.5.1 for DSL Developer
Please make sure that you put the method into an interface and not into a class. E.g. please set the interface flag on the JvmGenericType to true. That should do the trick.
What do you expect to be generated? A method can either have an implementation (default) or not (abstract). In fact, without any modifiers, an interface method is implicitly abstract.
interface MyInterface
{
public abstract void foo();
}
compiles to exactly the same bytecode as
interface MyInterface
{
public void foo();
}
I often see people use the keyword using in their Haxe code. It seem to go after the import statements.
For example, I found this is a code snippet:
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;
using Lambda;
What does it do and how does it work?
The "using" mixin feature of Haxe is also referred as "static extension". It's a great syntactic sugar feature of Haxe; they can have a positive effect on code readability.
A static extension allows pseudo-extending existing types without modifying their source. In Haxe this is achieved by declaring a static method with a first argument of the extending type and then bringing the defining class into context through the using keyword.
Take a look at this example:
using Test.StringUtil;
class Test {
static public function main() {
// now possible with because of the `using`
trace("Haxe is great".getWordCount());
// otherwise you had to type
// trace(StringUtil.getWordCount("Haxe is great"));
}
}
class StringUtil {
public static inline function getWordCount(value:String) {
return value.split(" ").length;
}
}
Run this example here: http://try.haxe.org/#C96B7
More in the Haxe Documentation:
Haxe static extensions in the Haxe Manual
Haxe static extensions tagged articles in the Haxe Code Cookbook
I have the following grammar:
Model: declarations += Declaration* statements += Statement*;
Declaration: 'Declare' name=ID;
Statement: 'Execute' what=[Declaration];
With that I can write simple scripts like:
Declare step_forward
Declare turn_right
Declare turn_left
Execute step_forward
Execute turn_left
Execute step_forward
Now I want that the java program provides all declarations, so that the script only contains the Execute statements. I read about IGlobalScopeProvider which seems to be the right tool for the job, but I have no idea how to add my data to it, and how to make Xtext use it.
So, how can I provide declarations from external to my grammar?
Update
My goal was somewhat unclear, so I try to make it more concrete. I want to keep the declarations as simple java objects, for instance:
List<Move> declarations = Arrays.asList(
new Move("step_forward"),
new Move("turn_right"),
new Move("turn_left"));
and the script should be:
Execute step_forward
Execute turn_left
Execute step_forward
I'm not really sure what you are asking for. After thinking about it, I cand derive th following possible questions:
1.) You want to split your script into two files. File a will only contain your declarations and File b then will only contain Statements. But any 'what' attribute will hold a reference to the declarations of File a.
This works out of the box with your grammar.
2.) You have any Java source code which provides a class which defines, for example a 'Declare Interface', and you want the 'what' attribute to reference to this interface or to classes which implement this interface.
Updated answer You should use Xbase within your language. There you can define that your 'what' attribute references to any Java type using the Xtypes rule 'JvmTypeReference'. The modifications you have to within your grammar are not that difficult, I think it could look this:
// Grammar now inherits from the Xbase grammar
// instead of the common terminals grammar
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Model:
declarator=Declarator?
statements+=Statement*;
Declarator:
'Declare' name=ID;
Statement:
'Execute' what=JvmTypeReference;
The, you can refer to any Java type (Java API, any linked API, user-defined types) by adressing them with their qualified name. It would look like this:
Referring to JVM types look like this in an Xtext language. (Screenshot)
You can also validate whether the referenced JVM type is valid, e.g. implements a desired interface which I would define with one single, optional declarator in the model.
Referenced JVM type is checked whether it is a valid type. (Screenshot)
With Xbase it is very easy to infer a Java interface for this model element. Use the generated stub '...mydsl.MyDslJvmModelInferrer':
class MyDslJvmModelInferrer extends AbstractModelInferrer {
#Inject extension JvmTypesBuilder
#Inject extension TypeReferences
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(
element.declaration.toInterface('declarator.' + element.declaration.name) [
members += it.toMethod("execute", TypesFactory.eINSTANCE.createJvmVoid.createTypeRef)[]
])
}
}
It derives a single interface, named individually with only one method 'execute()'.
Then, implement static checks like this, you should use the generated stub '...mydsl.validation.MyDslValidator' In my example it is very quick and dirty, but you should get the idea of it:
class MyDslValidator extends AbstractMyDslValidator {
#Check
def checkReferredType(Statement s) {
val declarator = (s.eContainer as Model).declaration.name
for (st : (s.what.type as JvmDeclaredType).superTypes) {
if (st.qualifiedName.equals('declarator.' + declarator)) {
return
}
}
(s.what.simpleName + " doesn't implement the declarator interface " + declarator).
warning(MyDslPackage.eINSTANCE.statement_What)
}
}
(I used the preferred Xtend programming language to implement the static checking!) The static check determines if the given JvmTypeReference (which is a Java class from your project) implements the declared interface. Otherwise it will introduce a warning to your dsl document.
Hopefully this will answer your question.
Next update: Your idea will not work that well! You could simply write a template with Xtend for that without using Xbase, but I cannot imagine how to use it in a good way. The problem is, I assume, you don't to generate the hole class 'Move' and the hole execution process. I have played around a little bit trying to generate usable code and seems to be hacky! Neverthess, here is my solution:
Xtext generated the stub '...mydsl.generator.MyDslGenerator' for you with the method 'void doGenerate'. You have to fill this method. My idea is the following: First, you generate an abstract and generic Executor class with two generic parameters T and U. My executor class then has an abstract method 'executeMoves()' with the return value T. If this should be void use the non-primitive 'Void' class. It holds your List, but of the generic type u which is defined as a subclass of a Move class.
The Move class will be generated, too, but only with a field to store the String. It then has to be derived. My 'MyDslGenerator' looks like that:
class MyDslGenerator implements IGenerator {
static var cnt = 0
override void doGenerate(Resource resource, IFileSystemAccess fsa) {
cnt = 0
resource.allContents.filter(typeof(Model)).forEach [ m |
fsa.generateFile('mydsl/execution/Move.java', generateMove)
fsa.generateFile('mydsl/execution/Executor' + cnt++ + '.java', m.generateExecutor)
]
}
def generateMove() '''
package mydsl.execution;
public class Move {
protected String s;
public Move(String s) {
this.s = s;
}
}
'''
def generateExecutor(Model m) '''
package mydsl.execution;
import java.util.List;
import java.util.Arrays;
/**
* The class Executor is abstract because the execution has to implemented somewhere else.
* The class Executor is generic because one does not know if the execution has a return
* value. If it has no return value, use the not primitive type 'Void':
* public class MyExecutor extends Executor_i<Void> {...}
*/
public abstract class Executor«cnt - 1»<T, U extends Move> {
#SuppressWarnings("unchecked")
private List<U> declarations = Arrays.<U>asList(
«FOR Statement s : m.statements»
(U) new Move("«s.what.name»")«IF !m.statements.get(m.statements.size - 1).equals(s)»,«ENDIF»
«ENDFOR»
);
/**
* This method return list of moves.
*/
public List<U> getMoves() {
return declarations;
}
/**
* The executor class has to be extended and the extending class has to implement this
* method.
*/
public abstract T executeMoves();
}'''
}