I've run into a situation where I would like to have an extension method that will work on any class that implements at least two interfaces.
Example:
class Named {
String get name;
}
class Gender {
String get gender;
}
extension ExampleExtension on Named, Gender {
String get info => "$name is a $gender"
}
However, I cannot seem to have an extension on more than on class. Is this possible in dart?
It's not possible.
You can only add extensions to types, and (Named and Gender) is not a type.
What you are asking for is "intersection types", which Dart does not have.
You need to introduce an actual type which extends both Named and Gender in order to have something to attach the extension to.
Related
This is a generalized question about a best practice in a Flutter app in Dart.
Consider I have a basic model.
class Person {
String fname;
String lname;
String phone;
...
}
This class has a lot of other public and private properties, but all of them are derived from these three public properties. I want to persist a List{Person} to file but I only need to store these three public properties for each Person.
One idea I have is to create a Convert class that takes a Person and creates a simpler class that only has the three properties which gets persisted to the file. The same Convert class would take the same simpler class and create a Person class.
Is this the best approach?
Does there happen to be a framework that does this for me?
Use JSON. Add toJson to your class.
class Person {
String fname;
String lname;
String phone;
Map<String, dynamic> toJson() => {'fname': fname, 'lname': lname', ... };
}
Then you can use jsonEncode to get a String.
We have some packages to make creating the toJson/fromJson bits easier. See https://pub.dartlang.org/packages/json_serializable
I am trying to declare the class as public as shown below
class RewardsAndRedemptionModel:BaseObject {
var rewardHistory :[RewardHistoryModel]!
}
This is where i am trying to make the class public but i could not.
public class RewardHistoryModel :BaseObject {
var rewardValue : String!
var recordedByName : String!
var rewardFor : String!
}
Even i read the documentations available on Internet i couldn't get it please help me out.
The compiler tells you that you can't make it public because the super class is internal. The compiler isn't lying, you know.
No subclass can be more accessible than its super class. Why? Think about it this way, a subclass has all the properties and members that its super class has. If this restriction didn't exist, then access modifiers will not be as useful anymore. You have an internal class. Someone else subclasses your internal class and declare it as public. Now encapsulation is broken. Things that you don't want to be accessed can now be accessed through the subclass.
In other words, if a subclass is more accessible than its super class, then the access modifier of the super class loses effect. That's why the compiler has this restriction: to remind you that what you're writing can make the super class' access modifier lose effect. You're probably doing the wrong thing.
To fix the problem, declare BaseClass and its super classes as public.
Swift 3
You need to declare the access level of the RewardHistoryModel & BaseObject class & their internal members like functions or variables as public or open (open is available in swift 3).
public class BaseObject {
// set member of this class as public that you want to access outside (Project/Framework Level)
}
public class RewardHistoryModel :BaseObject {
// set members as public or open - // open is available in swift 3.
public (or open) var rewardValue : String!
public (or open) var recordedByName : String!
public (or open) var rewardFor : String!
}
As stated in the documentation (The Swift Programming Language - Access Control) :
A public variable cannot be defined as having an internal or private
type, because the type might not be available everywhere that the
public variable is used.
Classes are declared as internal by default, so you have to add the public keyword to make them public.
A similar rule exists for functions as well.
A function cannot have a higher access level than its parameter types and return type, because the function could be used in situations where its constituent types are not available to the surrounding code.
You have to declare BaseObject class as public as well.
I want to include "created" and "modified" fields in all of my domain classes and would like to embrace the DRY principle if possible. I don't want to extend each class as I cannot do that for a second time so instead, I'm trying to implement an interface.
Consider:
interface AutoTimeStamp{
Date created
Date modified
}
class Dog implements AutoTimeStamp{
String breed
}
class Cat implements AutoTimeStamp{
String noOfLives
}
But when I try to create a new Dog or Cat I get:
Cannot set the property 'created' because the backing field is
final.
Any idea as to why this appears to be illegal? This works like a charm as an extended class.
The above answer is correct, to define an interface that has properties you need to do:
interface AutoTimeStamp{
Date getCreated()
void setCreated(Date created)
Date getModified()
void setModified(Date modified)
}
Then in the implementing classes you can define the properties
Date created
Date modified
dateCreated and lastUpdated is available in domain class by default.
If you want to disable autoTimestamp then use:
static mapping = {
autoTimestamp false
}
You should not forget that Groovy interfaces follow the same philosophy as Java interfaces. As such for every property in a Groovy interface:
PropertyType propertyName
means:
public static final PropertyType propertyName
You can also have a look here, please have a look on Guillaume Laforge's posts ;)
http://groovy.329449.n5.nabble.com/RFE-interface-properties-td386038.html
I have a bunch of subclasses like so:
abstract class Fruit {
...
String getType() {
// get the discriminator value for this type
GrailsDomainBinder.getMapping(this.class).discriminator
}
}
class Apple extends Fruit {
static mapping = {
discriminator 'Apple'
}
}
class Pear extends Fruit {
static mapping = {
discriminator 'Pear'
}
}
In other words, Fruit is a base type with Apple and Pear as subtypes. I exposed a type property that gets the discriminator value that's set in the subclasses.
Now I have a JsonExportService that exports an instance as JSON data. When I'm running the application, this service correctly exports the type property filled in with the discriminator value.
I now need to write a unit test for JsonExportService. Problem is, GrailsDomainBinder doesn't seem to be mocked out in unit tests, and I'm getting NPE: cannot access discriminator property on a null object.
I can work around it in two ways:
Create a static property in each subclass that has the same value as the discriminator:
class Pear extends Fruit {
static String type = 'Pear'
...
}
This seems really hacky though, and I'm declaring the same value in two places.
Change the getType() code to:
GrailsDomainBinder.getMapping(this.class)?.discriminator
This works, but now I'm basically ignoring the discriminator altogether, and the unit test is not 'complete' because it requires a follow-up integration test to ensure that the getType() method is returning the correct value.
Does anyone know of a better, unit-testing-friendly way of getting the discriminator value from the domain mapping?
It seems that DefaultGrailsDomainConfiguration is the responsible to initializing the mappings, so you can try:
def domainClass = grailsApplication.getDomainClass(Pear.class.name)
DefaultGrailsDomainConfiguration.configureDomainBinder(grailsApplication, [domainClass] as Set)
println GrailsDomainBinder.getMapping(Pear)
I have a base class which is a Riak entity, and some derived classes that extends BaseEntity.
BaseEntity has a field named Identifier which is annotated as #RiakKey.
but apparently this is not enought. It seems that I must have the Identifier field, with the #RuiakKey anotation in each derived class, otherwise I get this exception:
com.basho.riak.client.convert.NoKeySpecifedException
at com.basho.riak.client.bucket.DefaultBucket.fetch(DefaultBucket.java:535)
at com.att.cso.omss.datastore.riak.controllers.RiakBaseController.isEntityExist(RiakBaseController.java:130)
at com.att.cso.omss.datastore.riak.controllers.RiakBaseController.createEntity(RiakBaseController.java:94)
at com.att.cso.omss.datastore.riak.controllers.RiakBaseController.createServiceProvider(RiakBaseController.java:234)
at com.att.cso.omss.datastore.riak.App.serviceProviderTests(App.java:62)
at com.att.cso.omss.datastore.riak.App.main(App.java:38)
So, my current implementation looks like this (duplication of the identifier field):
public class BaseEntity{
#RiakKey
#JsonProperty("Id")
protected String identifier;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
.
.
.
public class Service extends BaseEntity{
#RiakKey
#JsonProperty("Id")
protected String identifier;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
is there a way to avoid this duplication?
UPDATED: Thanks to a contribution by someone who saw this question, this is will now be supported as of the 1.0.7 client release. See: https://github.com/basho/riak-java-client/pull/180
Original Answer:
Short answer: No, there's not a way around it currently.
The reason is the com.basho.riak.client.convert.reflect.AnnotationScanner class, how it looks for these annotations, and what we allow the scoping of the fields to be.
It uses Class.getDeclaredFields() which only gets the fields explicitly declared in the class, not inherited ones. The reason for this is that it gets private and protected members, whereas Class.getFields() would get inherited ones but only if they were declared public in a parent class.
One simple way around this would be to recursively scan each parent class up the inheritance tree. Because of how we cache the annotated fields for domain objects this would only be a one time hit and probably wouldn't be too terrible of a thing to do.
If this is something you'd be interested in having added to the client, please feel free to open an issue on github (or code & submit it yourself, of course - we're always thankful for community submissions!).