In Dart we can use generic classes [class]. We can also specialize those classes [class]. However at runtime the specialization is not used. (In C++ this is called template programming)
Example: The following code will result in the output
Hallo world
How are you
class MyClass<T> {
foo( print('Hallo world'); );
}
class MyClassInt implements MyClass<int> {
#override
foo( print('How are you'); );
}
main() {
MyClass<int> a = Myclass<int>();
MyClassInt b = MyClassInt();
a.foo();
b.foo();
}
How can the specialization (here type [int]) be done, that it is called at runtime, i.e.
main() {
MyClass<int> a = Myclass<int>();
a.foo();
}
should result in the outcome "How are you".
As mentioned by jamesdlin, Dart does not support specialization. But you can do something like this to make the illusion:
class MyClass<T> {
factory MyClass() {
if (T == int) {
return MyClassInt() as MyClass<T>;
} else {
return MyClass._();
}
}
// Hidden real constructor for MyClass
MyClass._();
void foo() {
print('Hallo world');
}
}
class MyClassInt implements MyClass<int> {
#override
void foo() {
print('How are you');
}
}
void main() {
final a = MyClass<int>();
final b = MyClassInt();
final c = MyClass<String>();
a.foo(); // How are you
b.foo(); // How are you
c.foo(); // Hallo world
}
Related
How to check type of Super class with Child class instance? I have below example and don't want to use dart-mirrors.
class SomeClass{
}
class SomeOtherClass extends SomeClass{
}
void main() {
var s1 = new SomeOtherClass();
someMethod(SomeClass, s1);
}
void someMethod(Type t, dynamic instance){
print(instance.runtimeType == t);
//print(instance.runtimeType is t); Does not work!
}
Update
Just today the package reflectable was released which allows to do this like with mirrors, but a transformer generates code instead to avoid using mirrors in production.
import 'package:reflectable/reflectable.dart';
// Annotate with this class to enable reflection.
class Reflector extends Reflectable {
const Reflector()
: super(typeCapability); // Request the capability to invoke methods.
}
const reflector = const Reflector();
#reflector
class SomeClass{
}
#reflector
class SomeOtherClass extends SomeClass{
}
void someMethod(Type t, dynamic instance){
InstanceMirror instanceMirror = reflector.reflect(instance);
print(instanceMirror.type.isSubclassOf(reflector.reflectType(t)));
}
void main() {
var s1 = new SomeOtherClass();
someMethod(SomeClass, s1);
}
Original
It might be directly supported when https://github.com/gbracha/metaclasses is implemented.
Currently this workaround can be used:
class IsInstanceOf<E> {
bool check(t) => t is E;
}
void someMethod(Type t, dynamic instance){
print( new IsInstanceOf<t>().check(instance));
//print(instance.runtimeType is t); Does not work!
}
This runs fine and returns the correct result but the analyzer shows a warning because t can't be used as a type.
If you wrap SomeClass in a generic class it works without a warning
class SomeClass{
}
class SomeOtherClass extends SomeClass{
}
void main() {
var s1 = new SomeOtherClass();
someMethod(new IsInstanceOf<SomeClass>(), s1);
}
void someMethod(IsInstanceOf t, dynamic instance){
print(t.check(instance));
//print(instance.runtimeType is t); Does not work!
}
class IsInstanceOf<E> {
bool check(instance) => instance is E;
}
Try it at DartPad
I want to implement call cache(memoization) in non-intrusive way with the metadata annotations.
Hopefully, it will work like this:
class A{
#Cached
foo(msg) {
return msg;
}
}
void main() {
#Cached
var foo = ()=>"hello";
}
Can it be achieved with only dart:mirrors ?
I wrote a whole blog post on this topic a while ago. It's too long to copy here, so here's the link:
http://dartery.blogspot.com/2012/09/memoizing-functions-in-dart.html
The upshot is that you can write higher-order memoizing functions, but they're limited in generality by Dart's lack of flexible args functions. Also, if you want to use dynamic programming with recursive functions, you need to write your function with memoization in mind - it needs to take itself as an argument, so you can pass in the memoized version.
My current solution allows:
class B {
#CachedCallName(#cachedBaz)
baz() => print("first call to baz");
}
class A extends B with CacheableCalls {
#CachedCallName(#foo)
_foo(msg) {
print("first call with: $msg");
return msg + msg;
}
}
void main() {
A a = new A();
print(a.foo(21));
print(a.foo(21));
a.cachedBaz();
print(a.foo(22));
a.cachedBaz();
}
Output:
first call with: 21
42
42
first call to baz
first call with: 22
44
Flaws:
- can't cache methods with their actual names.
- can extend collection view but can't cache existing operators like operator []
- can't cache functions.
Full source:
#MirrorsUsed(metaTargets: CachedCallName)
import 'dart:mirrors';
class CachedCallName {
final Symbol name;
const CachedCallName(this.name);
}
#proxy
class CacheableCalls {
Map _cache = new Map();
dynamic _chacheInvoke(InstanceMirror thisMirror, Symbol
methodName, Invocation invocation) {
String key = "$methodName${invocation.positionalArguments}"
"${invocation.namedArguments}";
if (_cache.containsKey(key)) {
return _cache[key];
} else {
InstanceMirror resultMirror = thisMirror.invoke(methodName,
invocation.positionalArguments, invocation.namedArguments);
_cache[key] = resultMirror.reflectee;
return resultMirror.reflectee;
}
}
dynamic noSuchMethod(Invocation invocation) {
bool isFound = false;
var result;
Symbol called = invocation.memberName;
InstanceMirror instanceMirror = reflect(this);
ClassMirror classMirror = instanceMirror.type;
classMirror.instanceMembers.forEach((Symbol name, MethodMirror mm) {
mm.metadata.forEach((InstanceMirror im) {
if (im.reflectee is CachedCallName) {
if (im.reflectee.name == called) {
isFound = true;
result = _chacheInvoke(instanceMirror, name, invocation);
}
}
});
});
if (isFound) {
return result;
} else {
throw new NoSuchMethodError(this, called,
invocation.positionalArguments, invocation.namedArguments);
}
}
}
class B {
#CachedCallName(#cachedBaz)
baz() => print("first call to baz");
}
class A extends B with CacheableCalls {
#CachedCallName(#foo)
_foo(msg) {
print("first call with: $msg");
return msg + msg;
}
}
void main() {
A a = new A();
print(a.foo(21));
print(a.foo(21));
a.cachedBaz();
print(a.foo(22));
a.cachedBaz();
}
Is there way to overriding method in Dart like JAVA, for example:
public class A {
public void handleLoad() {
}
}
And when overriding:
A a = new A() {
#Override
public void handleLoad() {
// do some code
}
};
No, Dart does not have anonymous classes. You have to create a class that extends A and instantiate it.
No but it much less useful in Dart because you can just reassign function:
typedef void PrintMsg(msg);
class Printer {
PrintMsg foo = (m) => print(m);
}
main() {
Printer p = new Printer()
..foo('Hello') // Hello
..foo = ((String msg) => print(msg.toUpperCase()))
..foo('Hello'); //HELLO
}
However you will need some extra boilerplate to access instance.
Use type Function:
class A {
final Function h
A(this.h);
void handleLoad(String loadResult) { h(loadResult); }
}
Or
class A {
final Function handleLoad;
A(this.handleLoad);
}
A a = new A((String loadResult){
//do smth.
});
I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.
I have this class:
class GenericController <T extends RequestHandler> {
void processRequest() {
T t = new T(); // ERROR
}
}
I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.
This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:
You define a method type like this: (Add params if necessary)
typedef S ItemCreator<S>();
or even better:
typedef ItemCreator<S> = S Function();
Then in the class that needs to create the new instances:
class PagedListData<T>{
...
ItemCreator<T> creator;
PagedListData(ItemCreator<T> this.creator) {
}
void performMagic() {
T item = creator();
...
}
}
Then you can instantiate the PagedList like this:
PagedListData<UserListItem> users
= new PagedListData<UserListItem>(()=> new UserListItem());
You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.
You can use similar code:
import "dart:mirrors";
void main() {
var controller = new GenericController<Foo>();
controller.processRequest();
}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T();
T t = Activator.createInstance(T);
t.tellAboutHimself();
}
}
class Foo extends RequestHandler {
void tellAboutHimself() {
print("Hello, I am 'Foo'");
}
}
abstract class RequestHandler {
void tellAboutHimself();
}
class Activator {
static createInstance(Type type, [Symbol constructor, List
arguments, Map<Symbol, dynamic> namedArguments]) {
if (type == null) {
throw new ArgumentError("type: $type");
}
if (constructor == null) {
constructor = const Symbol("");
}
if (arguments == null) {
arguments = const [];
}
var typeMirror = reflectType(type);
if (typeMirror is ClassMirror) {
return typeMirror.newInstance(constructor, arguments,
namedArguments).reflectee;
} else {
throw new ArgumentError("Cannot create the instance of the type '$type'.");
}
}
}
I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.
class foo<T> {
void foo(T Function() creator) {
final t = creator();
// use t
}
}
P.S. inspired by Patrick's answer
2022 answer
Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.
So what you could do is:
class MyClass<T> {
final T Function() creator;
MyClass(this.creator);
T getGenericInstance() {
return creator();
}
}
and when using it:
final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)
Nothing different but looks cleaner imo.
Here's my work around for this sad limitation
class RequestHandler {
static final _constructors = {
RequestHandler: () => RequestHandler(),
RequestHandler2: () => RequestHandler2(),
};
static RequestHandler create(Type type) {
return _constructors[type]();
}
}
class RequestHandler2 extends RequestHandler {}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T(); // ERROR
T t = RequestHandler.create(T);
}
}
test() {
final controller = GenericController<RequestHandler2>();
controller.processRequest();
}
Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.
Working with FLutter
typedef S ItemCreator<S>();
mixin SharedExtension<T> {
T getSPData(ItemCreator<T> creator) async {
return creator();
}
}
Abc a = sharedObj.getSPData(()=> Abc());
P.S. inspired by Patrick
simple like that.
import 'dart:mirrors';
void main(List<String> args) {
final a = A<B>();
final b1 = a.getInstance();
final b2 = a.getInstance();
print('${b1.value}|${b1.text}|${b1.hashCode}');
print('${b2.value}|${b2.text}|${b2.hashCode}');
}
class A<T extends B> {
static int count = 0;
T getInstance() {
return reflectClass(T).newInstance(
Symbol(''),
['Text ${++count}'],
{Symbol('value'): count},
).reflectee;
}
}
class B {
final int value;
final String text;
B(this.text, {required this.value});
}
Inspired by Patrick's answer, this is the factory I ended up with.
class ServiceFactory<T> {
static final Map<Type, dynamic> _cache = <String, dynamic>{};
static T getInstance<T>(T Function() creator) {
String typeName = T.toString();
return _cache.putIfAbsent(typeName, () => creator());
}
}
Then I would use it like this.
final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());
Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.
MyStack()
{
Vector<Integer> v=new Vector<Integer>(10,2);
}
void push(int n)
{
v.addElement(n);
}
void pop()
{
if(v.isEmpty())
System.out.println("Stack underflow!");
else
System.out.println(v.elementAt(0));
}
void display()
{
for(int i=0;i<v.size();i++)
System.out.print(v.elementAt(i) +" ");
}
}
class StackDemo
{
public static void main(String args[])
{
Scanner in=new Scanner(System.in);
MyStack s=new MyStack();
int option=0;
do
{
System.out.println("1: Push\n2:Pop\n3:Display\n4:Quit");
System.out.println("Enter your option: ");
option=in.nextInt();
switch(option)
{
case 1:
{
System.out.println("Enter an integer:");
int n=in.nextInt();
s.push(n);break;
}
case 2:s.pop();break;
case 3:s.display();break;
}
}
while(option!=4);
}
}
// throws an error: variable v not found. Any help would be much appreciated.Thanks.
It looks like v is being created locally in your constructor instead of as a member of your class.
Try defining v as a class member and then simply assign it in your constructor.
class MyStack {
Vector<Integer> v;
public MyStack() {
v = new Vector<Integer>(10,2);
}
}
Or just assign it when you define it:
class MyStack {
Vector<Integer> v = new Vector<Integer>(10,2);
}
Check out the Java tutorial on class members.