I have a custom class:
private class FeatureIndexEntry {
public String feature_name;
public int index;
public FeatureIndexEntry (String feature_name) {
this.feature_name = feature_name;
}
#Override
public int hashCode () {
return this.feature_name.hashCode();
}
#Override
public boolean equals (Object o) {
if (this == o) return true;
if (o instanceof String) {
return ((String) o).equals(this.feature_name);
}
if (!(o instanceof FeatureIndexEntry)) return false;
return ((FeatureIndexEntry) o).feature_name.equals(this.feature_name);
}
}
I also have HashSet<FeatureIndexEntry> feature_index;, there is something already in it.
feature_index.add( new FeatureIndexEntry("gx") );
feature_index.add( new FeatureIndexEntry("gy") );
Now I want to check whether String "gx" in it, I called feature_index.contains("gx"), but the result is false.
After overriding method hashCode() and equals(), why it still cannot search String in it?
I checked the result o instanceof String in equals, which is always false, but no other types are involved.
When I create a new FeatureIndexEntry object it works fine.
FeatureIndexEntry a = new FeatureIndexEntry("gx");
feature_index.contains(a); // true
Thanks for your reading, any help will be appreciated.
You are actually adding Objects of FeatureIndexEntry, and seraching for String type object. How will this work..??
Modify your Test class as below.
public class Test2 {
public static void main(String[] args) {
HashSet<FeatureIndexEntry> feature_index = new HashSet<FeatureIndexEntry>();
FeatureIndexEntry f1 = new FeatureIndexEntry("gx");
FeatureIndexEntry f2 = new FeatureIndexEntry("gy");
feature_index.add(f1);
feature_index.add(f2);
System.out.println(feature_index.contains(f1));
}
}
Related
In my Activity, I have a Training object member initialized during onCreate(). All the members of this object are set.
private Training mTraining; is a class member
public class Training extends BaseModel {
...
#SerializedName("state")
public TrainingState state;
....
public TrainingPreview() {
}
This object is got from server (JSON), and I had a converter on this state to ensure this enum can't be null (I use GSON engine):
public class TrainingStateConverter extends EnumConverter<TrainingState> {
public static final Type TYPE = new TypeToken<TrainingState>() {}.getType();
#Override
protected TrainingState deserialize(String value) {
return TrainingState.fromString(value);
}
#Override
protected TrainingState getUnknownValue() {
return TrainingState.UNKNOWN;
}
}
During the setup, I've created the exercise list with the listener to show a specific exercise:
private void refreshExercisesList() {
final Runnable showTrainingParts = new Runnable() {
public void run() {
int nbItems = mCardExercises.setExercises(mTraining.training, mTraining.state,
new FlatCardTrainingProfilePartExercisesView.OnClickExerciseListener() {
#Override
public void showPart(String trainingPartId, int index) {
onClickOnExercisesList(trainingPartId, index);
}
});
}
};
}
...
}
My onClickOnExercisesList() method:
private void onClickOnExercisesList(String trainingPartId, int index) {
...
switch (mTraining.state) {
...
This Activity code works perfectly since couple of months, but yesterday there was a NullPointerException on switch (mTraining.state) :
int com.xxx.model.training.TrainingState.ordinal()' on a null object reference
com.xxx.ui.training.TrainingActivity.onClickOnExercisesList
How is possible guys?
Thank you very much for your help!
This would occur if state did not appear in the JSON.
The TypeConverter is only used if there is a value in the JSON to convert. If the value isn't present, then there's nothing to convert, so the value is whatever the default is, which is null, because you didn't set it:
#SerializedName("state")
public TrainingState state;
To fix the issue, initialize the variable to a default value:
#SerializedName("state")
public TrainingState state = TrainingState.UNKNOWN;
I have a form with 4 possible options that need to be checked (could be less as well depending on circumstances). There are 2 editexts, one for email and one for a reference field when creating an order.
The email and reference fields may or may not be left empty based on conditions (which are available when the form is created). Additionally we may need to show an alert dialog to tell the user that it may not be possible to show the reference value (to the recipient of the order) and they may also need to agree to a terms and condition alert dialog.
Currently the onConfirm checks is something like this,
void onCreateOrderConfirmed(#Nullable final String receiverEmail,
#Nullable final String reference,
#Nullable final Boolean noRefAgreed,
#Nullable final Boolean termsAndConditionsAgreed) {
if (!reviewCompletionState.emailRequirementSatisfied()) {
if (!isValidEmail(receiverEmail)) {
view.showEmailError();
return;
}
reviewCompletionState = reviewCompletionState.newBuilder()
.receiverEmail(receiverEmail)
.emailRequirementSatisfied(true)
.build();
}
if (!reviewCompletionState.referenceRequirementSatisfied()) {
if (isEmpty(reference)) {
view.showReferenceError();
return;
}
reviewCompletionState = reviewCompletionState.newBuilder()
.reference(reference)
.referenceRequirementSatisfied(true)
.build();
}
if (!reviewCompletionState.noRefAgreed()) {
if (noRefAgreed == null || !noRefAgreed) {
view.showNoReferenceAlert();
return;
}
reviewCompletionState = reviewCompletionState.newBuilder()
.noRefAgreed(true)
.build();
}
if (!reviewCompletionState.termsAndConditionsAgreed()) {
if (termsAndConditionsAgreed == null || !termsAndConditionsAgreed) {
view.showTermsDisclaimerAlert();
return;
}
reviewCompletionState = reviewCompletionState.newBuilder()
.termsAndConditionsAgreed(true)
.build();
}
createOrder();
}
I would love to know if there is a way to make this validation simpler with RxJava2? (but don't currently know enough to be able to do this)
TIA
This can be a simple. There will be a lot of code, I'll show the result first.
private ReviewValidator reviewValidator = new ReviewValidator();
void onCreateOrderConfirmed(#Nullable final String receiverEmail,
#Nullable final String reference,
#Nullable final Boolean noRefAgreed,
#Nullable final Boolean termsAndConditionsAgreed) {
ReviewState reviewState = new ReviewState(receiverEmail,
reference,
noRefAgreed,
termsAndConditionsAgreed);//another model for simplicity
reviewValidator.validate(reviewState)
.flatMap(reviewState -> /* create order */)
.subscribe(this::onOrderCreated, this::onOrderCreatingError);
}
void onOrderCreated(Object order) {//or what you need here
//handle positive result
}
void onOrderCreatingError(Throwable throwable) {
if (throwable instanceof ValidateException) {
List<ValidateError> errors = ((ValidateException) throwable).getValidateErrors();
for (ValidateError error: errors) {
switch (error.getField()) {
case EMAIL: {
view.showEmailError();
return;//or break if you want show all errors
}
case REFERENCE: {
view.showReferenceError();
return;
}
//handle another errors....
}
}
//handle another error cases...
}
First, create model for reviewState:
public class ReviewState {
private String receiverEmail;
private String reference;
private Boolean noRefAgreed;
private Boolean termsAndConditionsAgree;
public ReviewState(String receiverEmail,
String reference,
Boolean noRefAgreed,
Boolean termsAndConditionsAgree) {
this.receiverEmail = receiverEmail;
this.reference = reference;
this.noRefAgreed = noRefAgreed;
this.termsAndConditionsAgree = termsAndConditionsAgree;
}
public String getReceiverEmail() {
return receiverEmail;
}
public String getReference() {
return reference;
}
public Boolean getNoRefAgreed() {
return noRefAgreed;
}
public Boolean getTermsAndConditionsAgree() {
return termsAndConditionsAgree;
}
}
Then create you own validator. It is not necessary to create a whole model, you can create validator for every field and and link them with flatMap(), your choice.
public class ReviewValidator extends Validator<ReviewState> {
#Override
protected List<ValidateFunction> getValidateFunctions(ReviewState reviewState) {
List<ValidateFunction> validateFunctions = new LinkedList<>();
validateFunctions.add(() -> validateEmail(reviewState.getReceiverEmail()));
validateFunctions.add(() -> validateReference(reviewState.getReference()));
//another validation methods
return validateFunctions;
}
private ValidateError validateEmail(String email) {
if (TextUtils.isEmpty(email)) {
return new ValidateError(Field.EMAIL);//Field.EMAIL - just enum
}
return null;
}
private ValidateError validateReference(String reference) {
if (TextUtils.isEmpty(reference)) {
return new ValidateError(Field.REFERENCE);
}
return null;
}
//....
//another validation methods
}
Abstract class for validator:
public abstract class Validator<Model> {
public Single<Model> validate(Model model) {
return Single.just(model)
.map(this::validateModel)
.flatMap(this::processResult);
}
private Single<Model> processResult(ValidateResultModel<Model> validateResultModel) {
return Single.create(subscriber -> {
List<ValidateError> validateErrors = validateResultModel.getValidateErrors();
if (validateErrors.isEmpty()) {
subscriber.onSuccess(validateResultModel.getModel());
} else {
subscriber.onError(new ValidateException(validateErrors));
}
});
}
private ValidateResultModel<Model> validateModel(Model model) {
List<ValidateError> errors = new LinkedList<>();
for (ValidateFunction validateFunctions : getValidateFunctions(model)) {
ValidateError error = validateFunctions.validate();
if (error != null) {
errors.add(error);
}
}
return new ValidateResultModel<>(model, errors);
}
protected abstract List<ValidateFunction> getValidateFunctions(Model model);
protected interface ValidateFunction {
#Nullable
ValidateError validate();
}
}
Helper classes for validator...
public class ValidateError {
private Field field;
public ValidateError(Field field) {
this.field = field;
}
public Field getField() {
return field;
}
}
class ValidateResultModel<T> {
private T model;
private List<ValidateError> validateErrors;
ValidateResultModel(T model, List<ValidateError> validateErrors) {
this.model = model;
this.validateErrors = validateErrors;
}
T getModel() {
return model;
}
List<ValidateError> getValidateErrors() {
return validateErrors;
}
}
public class ValidateException extends RuntimeException {
private List<ValidateError> validateErrors;
ValidateException(List<ValidateError> validateErrors) {
this.validateErrors = validateErrors;
}
public List<ValidateError> getValidateErrors() {
return validateErrors;
}
}
Initially, I took the idea from here: https://github.com/matzuk/TestableCodeMobius/tree/master/app/src/main/java/com/matsyuk/testablecodemobius/business/transfer/validation
i think you should the RxJava CombineLatest, so you need all of the form input are producing an observable, then you just combine it and adjust the view
as a reference you can check:
https://medium.com/#etiennelawlor/rxjava-on-the-sign-in-screen-9ecb66b88572
Using RxJava for email login validation, an observable is emitting twice
========
example:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxCompoundButton;
import com.jakewharton.rxbinding2.widget.RxTextView;
import io.reactivex.Observable;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText receiverText = findViewById(R.id.input_receiver);
EditText referenceText = findViewById(R.id.input_reference);
CheckBox checkRef = findViewById(R.id.check_ref);
CheckBox checkTerms = findViewById(R.id.check_terms);
Button buttonLogin = findViewById(R.id.button_login);
Observable<CharSequence> receiverObservable = RxTextView.textChanges(receiverText).skip(1); // can add more logic
Observable<CharSequence> referenceObservable = RxTextView.textChanges(referenceText).skip(1); // can add more logic
Observable<Boolean> refCheckObservable = RxCompoundButton.checkedChanges(checkRef); // can add more logic
Observable<Boolean> termsCheckObservable = RxCompoundButton.checkedChanges(checkTerms); // can add more logic
Observable<String> combineObservable = Observable.combineLatest(
receiverObservable,
referenceObservable,
refCheckObservable,
termsCheckObservable, (receiverCharSequence, referenceCharSequence, refBoolean, termsBoolean) -> {
// add logic here for now it is only combine the input
return receiverCharSequence + " " + referenceCharSequence + " " + refBoolean + " " + termsBoolean ;}
);
RxView.clicks(buttonLogin).flatMap(o -> { return combineObservable;}).distinctUntilChanged().subscribe(string -> {
Toast.makeText(this, string, Toast.LENGTH_LONG).show();
});
}
}
How can i achieve this without getting
error: cannot inherit from final ArraySet
ArraySet<MyObject>objectList = new ArraySet<MyObject>(){
#Override
public boolean equals(Object object) {
return ((MyObject)this).getId()==((MyObject)object ).getId();
}
};
ArraySet is a final class, meaning you can't inherit from it. You are trying to create an anonymous subclass when you write
ArraySet<MyObject>objectList = new ArraySet<MyObject>(){
#Override
public boolean equals(Object object) {
return ((MyObject)this).getId()==((MyObject)object ).getId();
}
};
What you really want to do is implement equals (and hashcode) for MyObject:
public class MyObject {
...
#Override
public boolean equals(Object other) {
if (other instanceof MyObject) {
return getId() == ((MyObject) other).getId();
}
return false;
}
}
Then, the ArraySet will work as you intend
I'm using a custom parcelable object called GameSettings to pass a number of settings between Activites within an Android app (developed using MonoDroid). The settings are stored as properties on this GameSettings class, and up until now they've all been simple integers which I've been able to parcel just fine using Parcel.WriteInt() and Parcel.ReadInt().
I've just added a new property to GameSettings called CelebrityNames which is of type List<string>, and I'm trying to pass this in the same way but when ReadStringList() is called the property gets populated with an empty list (despite a non-empty list being written to the parcel prior to this using WriteStringList()). The parcel is being passed from NameEntryActivity to GameRoundActivity.
GameSettings.cs
using System;
using System.Collections.Generic;
using Android.OS;
using Java.Interop;
using Object = Java.Lang.Object;
namespace Celebrities
{
public class GameSettings : Object, IParcelable
{
private static readonly GenericParcelableCreator<GameSettings> _creator
= new GenericParcelableCreator<GameSettings>((parcel) => new GameSettings(parcel));
[ExportField("CREATOR")]
public static GenericParcelableCreator<GameSettings> InitializeCreator()
{
return _creator;
}
public int NumberOfPlayers { get; set; }
public int NumberOfTeams { get; set; }
public int CelebritiesPerPlayer { get; set; }
public int SecondsPerRound { get; set; }
private List<string> _celebrityNames;
public List<string> CelebrityNames {
get
{
_celebrityNames.Shuffle ();
return _celebrityNames;
}
set
{
_celebrityNames = value;
}
}
public GameSettings (int players, int teams, int celebrities, int secondsPerRound)
{
NumberOfPlayers = players;
NumberOfTeams = teams;
CelebritiesPerPlayer = celebrities;
SecondsPerRound = secondsPerRound;
}
private GameSettings(Parcel parcel) : this(parcel.ReadInt (), parcel.ReadInt (), parcel.ReadInt (), parcel.ReadInt ())
{
if (_celebrityNames == null)
{
_celebrityNames = new List<string>();
}
parcel.ReadStringList (_celebrityNames);
}
public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
dest.WriteInt (NumberOfPlayers);
dest.WriteInt (NumberOfTeams);
dest.WriteInt (CelebritiesPerPlayer);
dest.WriteInt (SecondsPerRound);
dest.WriteStringList (_celebrityNames);
}
public int DescribeContents()
{
return 0;
}
}
}
Note: I'm using the backing variable _celebrityNames for parcelling as I have a custom getter that shuffles the list, which isn't necessary at this point. The problem is the same whether using the property or the variable.
GenericParcelableCreator.cs
using System;
using Android.OS;
using Object = Java.Lang.Object;
namespace Celebrities
{
public sealed class GenericParcelableCreator<T> : Object, IParcelableCreator
where T : Object, new()
{
private readonly Func<Parcel, T> _createFunc;
public GenericParcelableCreator(Func<Parcel, T> createFromParcelFunc)
{
_createFunc = createFromParcelFunc;
}
public Object CreateFromParcel(Parcel source)
{
return _createFunc(source);
}
public Object[] NewArray(int size)
{
return new T[size];
}
}
}
I'm including the relevant code from the Activity classes below (these are not the complete files for brevity, please ask if you think it would be helpful to see the rest too).
NameEntryActivity.cs (where I'm passing the parcel from)
public class NameEntryActivity : Activity
{
...
private GameSettings _gameSettings;
private List<string> _celebrityNames;
protected override void OnCreate (Bundle savedInstanceState)
{
...
_gameSettings = (Intent.Extras.GetParcelable ("GameSettings") as GameSettings);
_celebrityNames = new List<string> ();
...
}
...
private void MoveToNextCelebrity()
{
...
_gameSettings.CelebrityNames = _celebrityNames;
var intent = new Intent (this, typeof(GameRoundActivity));
intent.PutExtra("GameSettings", _gameSettings);
StartActivity (intent);
...
}
}
GameRoundActivity.cs (where I'm passing the parcel to)
public class GameRoundActivity : Activity
{
private GameSettings _gameSettings;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.GameRound);
_gameSettings = (Intent.Extras.GetParcelable ("GameSettings") as GameSettings);
}
}
This is my first time developing an Android app, so it may well be that I've made a mistake somewhere in implementing the parcelling framework or have misunderstood it. Equally I've been looking at this code for so long that maybe I'm just missing a more general silly mistake :)
Thanks in advance!
I switched to using a string array instead of a list and it's now working using Parcel.WriteStringArray() and Parcel.CreateStringArray().
Obviously this wouldn't be applicable in every situation though so I'm still interested in why this was happening!
I'm trying to pass a Parceble Extra to another activity using this example, but when I try get it on my second activity NullPointerExeception shows up, could somebody help me?
My Parcelable class:
public class MetaDados implements Parcelable {
private int codigoInstituicao;
// . . .
public MetaDados(int codigoInstituicao, int ano, String offlineUuid, String sigla, String nameInst,
String startedDate, String name, String finishedDate, long size) {
this.codigoInstituicao = codigoInstituicao;
// . . .
}
public int getCodigoInstituicao() {
return codigoInstituicao;
}
public void setCodigoInstituicao(int codigoInstituicao) {
this.codigoInstituicao = codigoInstituicao;
}
//getters and setters . . .
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(codigoInstituicao);
// . . .
}
public static final Parcelable.Creator<MetaDados> CREATOR = new Parcelable.Creator<MetaDados>() {
public MetaDados createFromParcel(Parcel in) {
return new MetaDados(in);
}
public MetaDados[] newArray(int size) {
return new MetaDados[size];
}
};
private MetaDados(Parcel in) {
codigoInstituicao = in.readInt();
//. . .
}
}
My AsynkTask how start my other activity:
ArrayList<MetaDados> metaDadosFull = new ArrayList<MetaDados>();
ArrayList<MetaDados> metaDadosPres = new ArrayList<MetaDados>();
Intent it = new Intent(activity, DownloadSelectionActivity.class);
it.putExtra("metaDadosFull", metaDadosFull);
it.putExtra("metaDadosPres", metaDadosPres);
activity.startActivity(it);
And my DownloadSelectionActivity where I try to get it:
ArrayList<MetaDados> fullList = (ArrayList<MetaDados>) getIntent().getParcelableExtra("metaDadosFull");
for (MetaDados metaDados : fullList) {
Log.d(Constants.DOWNLOAD_SELECTED_ACTIVITY, metaDados.getName());
}
ArrayList<MetaDados> presList = (ArrayList<MetaDados>) getIntent().getParcelableExtra("metaDadosPres");
for (MetaDados metaDados : presList) {
Log.d(Constants.DOWNLOAD_SELECTED_ACTIVITY, metaDados.getName());
}
Use Intent.putParcelableArrayListExtra() instead of putExtra(), and getParcelableArrayListExtra() instead of getParcelableExtra(). You can lose the casts as well, that may be where it's blowing up.