I recently converted an Android project from Java to Kotlin using Android Studio's Kotlin plugin. Post-conversion, many fields in my classes were highlighted, and hovering over them displays the message
This property has a backing field
Should I infer from the existence of this message/warning that it is always considered more idiomatic (in Kotlin) to use properties with explicitly-defined getters and/or setters? Or is the IDE being a little over-zealous with the informational messages? Answers that include references to official sources preferred :)
Idiomatic is to use properties without backing properties. Backing properties are an exception case, as are custom getters and setters. You do need to be aware of the difference between a "Backing Field" and "Backing Property":
In the Kotlin docs, it talks about Backing Fields which are something you sometimes access from custom getters/setters and exist for any given property. There is an implied name for backing fields of field, so in a setter you would access this backing field by that name:
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0)
field = value
}
A Backing Property on the other hand is something that you define to store the value in another private property and of which you have complete control over. By convention it would be private and be prefixed with an underscore _. This looks like:
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null)
_table = HashMap() // Type parameters are inferred
return _table ?: throw AssertionError("Set to null by another thread")
}
Nothing to worry about, it's a known issue with the IDE. The highlighting is there simply for your information, cause backing fields require some extra care in certain cases.
If it troubles you, just turn the inspection off. Or change the highlighting to be not so bright.
Related
Seems basic enough...
I have a custom JSF component and its associated renderer. The renderer does the decode and encodeEnd.
In decode i successfully retrie the submitted value via component.setSubmittedValue(ctx.getExternalContext().getRequestParameterMap().get(c.getClientId()));
In encodeEnd, i basically create the markup and if component.getValue() is not null, i insert its contents in the markup. So far so good.
Problem is that getValue() can be only be String. I have custom class that represents a compound data type and i want to use that as the component's local value. But doesn't work - JSF converts to String.
I also tried using component.getAttributes() - from the decode method, where i put my custom object in keyed to private static final String someKey = "asd". But later at encodeEnd there is no value/key in the map.
I want the users of this component to be able to specify in their backing bean the custom data type and not worry about serialization/deserialization to text representation between client/server. Presumably i have to use a converter for that? But how can i set up a default and immutable converter for the custom component?
The problem has a simple enough of a solution. Inside the Renderer class (or right into the Component class if using that directly):
#Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
SomeCustomObject thing;
//... do magic to create SomeCustomObject based on submittedValue
return thing;
}
Now whenever getValue() is called on that component, SomeCustomObject will be returned and you can safely cast to it. Also in the backing beans can use SomeCustomObject as the type.
Also note when calling component.getValue() in the actual Renderer, it will return SomeCustomObject as well, so if you're restoring state, you must convert back to its String representation.
For more details see and #Kukeltje's answer above and check how the same thing is done for primefaces calendar component here: https://github.com/primefaces/primefaces/blob/master/src/main/java/org/primefaces/component/calendar/BaseCalendarRenderer.java
For another more concise and clear illustration, check #BalusC's answer as well.
I've been working with converters in my PrimeFaces SelectOneMenu objects. They work fine if I only tell which class the converter is referring to:
#FacesConverter(forClass = DescriptorVO.class)
public class DescriptorVOConverter implements Converter { ... }
This way, I don't have to explicitly tell the JSF component which converter should be used when it is populated with objects of class DescriptorVO.
However, I made a page that used a p:SelectManyCheckbox and I couldn't for the life of me know why it wasn't calling my converter. Then I gave it a name, like so:
#FacesConverter(forClass = RoleVO.class, value = "roleVOConverter")
public class RoleVOConverter implements Converter { ... }
and passed it as one of the component's properties
<p:selectManyCheckbox id="cbx_roles" required="true" converter="roleVOConverter"
requiredMessage="At least one role must be selected."
value="#{userView.selectedRoles}" layout="responsive">
<f:selectItems value="#{userView.roles}" var="role"
itemLabel="#{role.title}" itemValue="#{role}" />
</p:selectManyCheckbox>
and voi la, it started calling the converter correctly. This raised a question to me regarding when I should name my converters (through the value attribute) and when telling them which class the converter should be used with (with the forClass attribute) is enough. I never had to namy any converters when working with PrimeFaces, only for this particular SelectManyCheckbox component. Do different components have different necessities regarding converters or did I just get the concept of converters wrong?
That can happen when the value of the UISelectMany component references a generic java.util.Collection type like List<Role> instead of an array like Role[]. This is specified in javadoc of UISelectMany (emphasis mine):
Obtain the Converter using the following algorithm:
If the component has an attached Converter, use it.
If not, look for a ValueExpression for value (if any). The ValueExpression must point to something that is:
An array of primitives (such as int[]). Look up the registered by-class Converter for this primitive type.
An array of objects (such as Integer[] or String[]). Look up the registered by-class Converter for the underlying element type.
A java.util.Collection. Do not convert the values.
If for any reason a Converter cannot be found, assume the type to be a String array.
The reason is, the generic type <Role> is lost during runtime and not directly determinable. It works when you're using MyFaces instead of Mojarra as it will in case of a java.util.Collection inspect the actual type by manually iterating over <f:selectItem(s)> and determine the Converter based on first non-null value.
I created spec issue 1422 on this to get this in JSF spec and improve Mojarra too.
See also:
UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T
Why does JSF put String values in a Map<..., Integer>? And how to work around it?
Use enum in h:selectManyCheckbox
With JSF, Managed Beans, & EL 2.2 I know generally that an expression of the form:
#{bean.value}
Will map to a corresponding set of functions in a managed bean class like so:
#ManagedBean
class Bean {
private String value;
public String getValue() { return value; }
public void setValue( String s ) { value = s; }
}
It is also possible to get and set properties of a map:
#{bean.value['key']}
Backed by something like:
#ManagedBean
class Bean {
private Map<String, Boolean> kvMap;
public boolean getValue( String key ) { return kvMap.get( key ); }
public void setValue( String key, boolean value ) { kvMap.put( key, value ); }
}
So far so good.
I'm finding as I spend more time with JSF however that I'm trying to write reusable chunks of code. Specifically, small blocks of xhtml in <ui:composition> blocks that I can include via <ui:include>. What's more, many of the more useful things for me are things like nested sets of checkboxes (our UI designer is just gaga over them ;-), and there <ui:repeat> becomes very handy.
Invariably, in order to use <ui:repeat> and <ui:include> without an ungodly amount of typing, I've been using aliases, either created via <ui:param> or inline with something like the var attribute of <ui:repeat>.
As I've been writing more and more nested UIComponents, particularly things that get their values from maps within maps, I'm finding it harder and harder to deduce the correct setter method signature that JSF will look for when submitting a form (for some reason writing getters seems to be more natural).
My question for you gurus then is:
Is there some way to get JSF to tell me what it expects a setter signature to look like? Since JSF generally doesn't complain about an expression that resolves to a getter-only (thinking it is a read-only property), I find the lack of feedback frustrating and it seems to require a lot of fiddling with different method signatures before I finally hit that magic right one.
I'm hoping there's some technique, say a FacesContext... query at runtime or looking in some compiled intermediate like a class file that would point me to the correct setter signature for a deeply nested property. If there is such a thing I think it would save me a lot of time trying to figure out how to get a setter constructed by trial and error.
Hopefully I've articulated clearly enough what I'm after, thanks in advance for your replies.
I understand that your question basically boils down to "How should a setter for a Map look like?".
The answer is simple: you don't need any one. EL uses the put() method on the Map itself. You only need to provide a getter for the whole Map. On getting map values, EL will use the get() method of the Map itself. This is all behind the scenes done by the builtin MapELResolver.
So this should do:
#ManagedBean
class Bean {
private Map<String, Boolean> kvMap;
public Map<String, Boolean> getValue() { return kvMap; }
}
which is to be used as #{bean.value['key']} or #{bean.value.key} if the key doesn't contain periods. You can just use it in input components as well.
<h:selectBooleanCheckbox value="#{bean.value.key}" />
As to the tooling, well, the JBoss Tools plugin for Eclipse has good EL autocomplete support for normal javabeans, but it can't autocomplete map keys. Further Eclipse has its own facilities to autogenerate bean properties along with getters and setters based on a list or existing properties.
I have a <h:selectBooleanCheckbox value="#{someBean.prop}"> where prop is a property of type int. Is it really not working straight away? Do I have to implement a custom converter to convert Boolean to int? Does anyone happen to have converter code for that at stock? I read that there is some bug in JSF 2.0 that prevents converters for <h:selectBooleanCheckbox> to be processed. Can anyone confirm that?
I use MyFaces 2, Tomahawk and Tomcat 6.
The <h:selectBooleanCheckbox> should, as its name say, be bound to a boolean or Boolean property. Nothing else. That it allows a converter attribute is actually a bug in the spec. It should never have allowed it.
The problem is more in your model, why would you use an int to represent a boolean state? Change your model to let it be a fullworthy boolean.
If changing the model isn't an option for some reason (a 3rd party API, a stupid architect, or stupid business restrictions, etc), then wrap the model getter/setter in the backing bean like follows
public boolean isChecked() {
return someModel.getSomeInt() != 0;
}
public void setChecked(boolean checked) {
someModel.setSomeInt(checked ? 1 : 0);
}
and use it instead as <h:selectBooleanCheckbox value="#{bean.checked}" />.
just to say MySQL doesn't have boolean as field type choice, and it could be an example for this need.
You can set field as tinyint in MySQL. Actually problem is getting method name when you create a boolean variable. Use method name getChecked() instead of isChecked().
I've been developing a few JSF applications lately and am disturbed with the inconsistency in the web component APIs.
I've noticed that there is extremely unpredictable behavior when calling .getValue() or .getSubmittedValue() on a JSF component object in server side code. Sometimes when I call .getValue() on a drop down list box, I've noticed that I get the value as it was BEFORE I selected my value (so the value from the last page refresh), of which .getSubmittedValue() gets me the correct value, as such:
UIInput name = new UIInput(); // This is the control I have in a bean.
public void submit(ActionEvent ae)
{
someMethod(name.getValue().toString()); // Retrieves the "old" value
someMethod(name.getSubmittedValue().toString()); // Retrieves the correct value
}
Also, I've noticed that calling .getSubmittedValue() on a form field sometimes results in a null pointer exception because that value has not been instantiated in the component object, in which case when I call .getValue() in that circumstance I get the correct value, for example:
HtmlInputText name = new HtmlInputText(); // This is the control I have in a bean.
public void submit(ActionEvent ae)
{
someMethod(name.getValue().toString()); // Retrieves the correct value
someMethod(name.getSubmittedValue().toString()); // Throws NullPointerException
}
Is this just a "quirk" of the JSF framework, or am I just using the API COMPLETELY incorrectly?? Any insight into these two methods would be greatly appreciated. Cheers.
Since this is the #1 result in Google for searching on getValue vs. getSubmittedValue I'd just like to add that the difference between these is critical in validation (i.e. when writing a custom validator)
To quote the API documentation for getSubmittedValue():
This is non-null only between decode
and validate phases, or when
validation for the component has not
succeeded. Once conversion and
validation has succeeded, the
(converted) value is stored in the
local "value" property of this
component, and the submitted value is
reset to null.
Source: http://myfaces.apache.org/core11/myfaces-api/apidocs/javax/faces/component/UIInput.html#getSubmittedValue()
This means that if the validation/conversion has taken place for the binding you are trying to access, you should call getValue() otherwise you'll have to call getSubmittedValue() and deal with parsing it yourself. The order in which these occur seems to be dictated by the order they appear in the UI, but I don't think that's guaranteed. Even if it is, you shouldn't count on that as changing field in your UI shouldn't break your code.
You can detect if the validation/conversion has been done by just looking at what isLocalValueSet() returns. If it returns true, then the valdation/conversion has been done, so you should call getValue(). Otherwise you'll need to call getSubmittedValue() and that'll give you the raw input the user entered and you'll likely want to parse it into something more meaningful.
For example, a calendar object would return a Date object when getValue() was called, but a String object when getSubmittedValue() was called. It's up to your converter to parse the string into a Date so it can be validated.
It'd be great if the JSF spec had a method which would do this for us, but AFAIK it doesn't. If certain dates need to be before other dates, and some are only required in certain circumstances, one will need to write several validators to handle this. So it can easily become an issue. This is similar to the fact that you can't do any kind of validation on a blank field, which means you can't make that field conditionally required. If validation was run on all fields, even blank ones, a custom validator could be written to throw an exception if it should be required and is not. There are some things with JSF which are just a pain; unless/until they're fixed, we just have to deal with them.
To speak to the specifics of the issue in the original post: the difference here is where you're at in the life cycle. The submit method seems like an action listener for a button, which puts it at the end of the life cycle; actions and action listeners are triggered in the "Invoke Application" phase which comes prior to the render response, but after validation. If you're going to program in JSF, you should learn and understand the life cycle. It's worth the time.
To quote the documentation on EditableValueHolder.getSubmittedValue:
Return the submittedValue value of
this component. This method should
only be used by the encodeBegin()
and/or encodeEnd() methods of this
component, or its corresponding
Renderer.
Generally, you would not even be calling getValue. Instead, the component's value attribute should be bound to your model (a bean, maybe). Your business logic would interact with the model, not the component.
If the submitted value is not being set as the value, then I'd guess that some validation is failing. The only problem with that is that your event is being fired. Two guesses for the problem here:
You have a stale reference to the component object.
You've set the immediate attribute on a UICommand which means that the event is fired in a phase where the component will be in an inappropriate state.
It isn't possible to be certain with the information provided.
I work on xpages which are based on JSF so.. it could be the same...
Anyway, getSubmittedValue(); always returns what you see in firebug/chrome develepers network tab. That is value within sent packet. I have it shown (chrome) in headers tab, in form data section, named $$xspsubmitvalue.
On the other hand, getValue() is component specific. <-- not 100% sure here.
TL;DR answer:
UIViewRoot viewRoot = context.getViewRoot();
UIInput input = (UIInput)viewRoot.findComponent(":form:inputID");
String inputValueString;
if (input.isLocalValueSet()) {
inputValueString = (String)input.getValue(); //validated and converted already
} else {
inputValueString = (String)input.getSubmittedValue(); //raw input
}
or at least that's what the other answers are saying to do...
Just use .getSubmittedValue() and deal with the consequences of having to convert raw input (if necessary, if that raw input needs conversion). .getValue() is broken in this regard, even with the code above. It delays the submitted value if you use it and that's unacceptable.