Kotlin modify the default constructor - kotlin

How would I achieve the following in Kotlin?
public class Person {
String firstName;
String lastName;
public Person(String name, String somethingElse) {
this.firstName = name.substring(0, 5);
this.lastName = name.substring(5, 10) + somethingElse;
}
}
The use case might be a bit weird, but this should be possible in Kotlin right? I have something like the following but then I get Conflicting overloads
data class Person(
val firstName: String,
val lastName: String
) {
constructor(name: String, somethingElse: String) :
this(firstName = name.substring(0, 5), lastName = name.substring(5, 10) + somethingElse)
}

You could have a companion object:
data class Person(
val firstName: String,
val lastName: String) {
companion object {
fun create(name: String, somethingElse: String): Person {
return Person(
name.substring(0, 5),
name.substring(5, 10) + somethingElse
)
}
}
}
fun main() {
val person = Person.create("Long first name", "Last name")
println(person)
}

Yes, it's possible:
data class Person(private val name: String, private val somethingElse: String) {
val firstName: String = name.substring(0, 5)
val lastName: String = name.substring(5, 10) + somethingElse
//You may want to override default "Person(name='$name', somethingElse='$somethingElse')"
override fun toString() = "Person(firstName='$firstName', lastName='$lastName')"
}

Related

Identifying relationships in Kotlin

Are nested classes a good way to model identifying relationships in Kotlin?
Requirements:
an issue can not be without a serno
a revocation can not be without an issue
an affirmation can not be without a revocation
Looks quite verbose:
class Have {
inner class Serno(val value: String) {
override fun toString(): String = "serno: $value"
fun nothing () = this#Have
inner class Issue(val value: String) {
override fun toString(): String = "issue: $value (${serno()})"
fun serno () = this#Serno
inner class Revocation(val value: String) {
override fun toString(): String = "revocation: $value (${issue()})"
fun issue () = this#Issue
inner class Affirmation(val value: String) {
override fun toString(): String = "affirmation: $value (${revocation()})"
fun revocation () = this#Revocation
}
}
}
}
}
val serno: Have.Serno = Have().Serno("123")
val issue: Have.Serno.Issue = serno.Issue("SUP-1")
val revocation: Have.Serno.Issue.Revocation = issue.Revocation("2020")
val affirmation: Have.Serno.Issue.Revocation.Affirmation = revocation.Affirmation("2022")
println(serno)
println(issue)
println(revocation)
println(affirmation)
println(serno == affirmation.revocation().issue().serno())
Is there a simpler way to achieve the same?
This would usually be achieved with simple non-null properties:
class Serno(val value: String) {
override fun toString(): String = "serno: $value"
}
class Issue(val value: String, val serno: Serno) {
override fun toString(): String = "issue: $value ($serno)"
}
class Revocation(val value: String, val issue: Issue) {
override fun toString(): String = "revocation: $value ($issue)"
}
class Affirmation(val value: String, val revocation: Revocation) {
override fun toString(): String = "affirmation: $value ($revocation)"
}
val serno = Serno("123")
val issue = Issue("SUP-1", serno)
val revocation = Revocation("2020", issue)
val affirmation = Affirmation("2022", revocation)
And if you're not strict on the toString format, you could even use the built-in toString of data classes, and simplify further:
data class Serno(val value: String)
data class Issue(val value: String, val serno: Serno)
data class Revocation(val value: String, val issue: Issue)
data class Affirmation(val value: String, val revocation: Revocation)

Unable to initialize kotlin class member from secondary function

class Person constructor(val first_name: String){
init{
println("Welcome, ${first_name}")
}
val last_name: String
constructor(fname: String, lname: String): this(fname){
this.last_name = lname
}
fun fullNameGreeting(){
println("Welcome, ${first_name} ${last_name}")
}
}
fun main() {
val kotlin = "🙂"
println(kotlin)
val adam: Person = Person("Adam", "Kim")
}
I get following error in Kotlin Playground:-
Property must be initialized or be abstract
Val cannot be reassigned
I am just trying it after reading the documentation on Kotlin website. I am still learning but I am not able to solve this problem. Please Help.
Property must be initialized or be abstract:
You got this error because you didn't initialize a value to last_name, you can't just declare the name and the type of the variable, you need to assign a default value to it like that val last_name: String = ""
Val cannot be reassigned:
And for this error because you set last_name as a value by using val and that means that you can't change it anymore, but you are trying to change it here this.last_name = lname, so to be able to change last_name you need to set is a variable by using var, like that var last_name: String = ""
So your final code should look like this:
class Person constructor(val first_name: String){
init{
println("Welcome, ${first_name}")
}
var last_name: String = ""
constructor(fname: String, lname: String): this(fname){
this.last_name = lname
}
fun fullNameGreeting(){
println("Welcome, ${first_name} ${last_name}")
}
}
fun main() {
val kotlin = "🙂"
println(kotlin)
val adam: Person = Person("Adam", "Kim")
}
If you want to create an instance of Person with only first_name or both first_name and last_name, you can use this approach:
class Person constructor(
val first_name: String,
val last_name: String = ""
){
init{
println("Welcome, ${first_name}")
}
fun fullNameGreeting(){
println("Welcome, ${first_name} ${last_name}")
}
}
This way you can either call Person("Your first name"), or Person("Your first name", "Your last name")
I'd suggest switching the constructors around. Then you can do
class Person constructor(val first_name: String, val last_name: String){
init{
println("Welcome, ${first_name}")
}
constructor(fname: String): this(fname, "")
fun fullNameGreeting(){
println("Welcome, ${first_name} ${last_name}")
}
}
or better yet, make the second parameter optional, like
class Person constructor(val first_name: String, val last_name: String = ""){
init{
println("Welcome, ${first_name}")
}
fun fullNameGreeting(){
println("Welcome, ${first_name} ${last_name}")
}
}
It seems that val is used for constant and var for variable (overwritable) properties. If you replace vals in the class with vars, it works.

Why retrofit returns null in response body?

Get null response results in response body. Using retrofit, dagger and repository. Why get null in response I don't know. My model seem right. Whats the problem?
MainViewModel.kt
#HiltViewModel
class MainViewModel#Inject constructor(
private val repository: MovieRepository,
#ApplicationContext private val context: Context
) : ViewModel() {
val movieList = MutableLiveData<Resource<Movie>>()
fun getAllMovies(movieName: String) {
movieList.postValue(Resource.Loading())
viewModelScope.launch {
try {
if (hasInternetConnection(context)) {
val response = repository.getMovies(movieName, "ffe9123f")
movieList.postValue(Resource.Success(response.body()!!))
} else
movieList.postValue(Resource.Error("Internet yok"))
} catch (ex: Exception) {
when (ex) {
is IOException -> movieList.postValue(Resource.Error("Network Failure " + ex.localizedMessage))
else -> movieList.postValue(Resource.Error("Conversion Error"))
}
}
}
}
}
Resource.kt
sealed class Resource<T>(
val data: T? = null,
val message: String? = null
) {
class Success<T>(data: T): Resource<T>(data)
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)
class Loading<T> : Resource<T>()
}
MovieRepository.kt
#Singleton
class MovieRepository #Inject constructor(private val movieAppService: MovieAppService) {
suspend fun getMovies(title: String, aKey: String): Response<Movie> = withContext(
Dispatchers.IO
) {
val movies = movieAppService.getMovies(title = title, aKey = aKey)
movies
}
Movie.kt
data class Movie(
val title: String,
val year: String,
val rated: String,
val released: String,
val runtime: String,
val genre: String,
val director: String,
val writer: String,
val actors: String,
val plot: String,
val language: String,
val country: String,
val awards: String,
val poster: String,
val ratings: List<Rating>,
val metascore: String,
val imdbRating: String,
val imdbVotes: String,
val imdbID: String,
val type: String,
val dvd: String,
val boxOffice: String,
val production: String,
val website: String,
val response: String
)
MovieAppService.kt
interface MovieAppService {
companion object {
const val ENDPOINT = "http://www.omdbapi.com/"
}
#GET(".")
suspend fun getMovies(#Query("t") title: String,#Query("apikey") aKey: String): Response<Movie>
}
{"Title":"A Beautiful Mind","Year":"2001","Rated":"PG-13","Released":"04 Jan 2002","Runtime":"135 min","Genre":"Biography, Drama","Director":"Ron Howard","Writer":"Akiva Goldsman, Sylvia Nasar","Actors":"Russell Crowe, Ed Harris, Jennifer Connelly","Plot":"After John Nash, a brilliant but asocial mathematician, accepts secret work in cryptography, his life takes a turn for the nightmarish.","Language":"English","Country":"United States","Awards":"Won 4 Oscars. 37 wins & 69 nominations total","Poster":"https://m.media-amazon.com/images/M/MV5BMzcwYWFkYzktZjAzNC00OGY1LWI4YTgtNzc5MzVjMDVmNjY0XkEyXkFqcGdeQXVyMTQxNzMzNDI#._V1_SX300.jpg","Ratings":[{"Source":"Internet Movie Database","Value":"8.2/10"},{"Source":"Rotten Tomatoes","Value":"74%"},{"Source":"Metacritic","Value":"72/100"}],"Metascore":"72","imdbRating":"8.2","imdbVotes":"908,920","imdbID":"tt0268978","Type":"movie","DVD":"25 Jun 2002","BoxOffice":"$170,742,341","Production":"N/A","Website":"N/A","Response":"True"}
Movie model is wrong. I didn't realize the starting letter is uppercase letter.
Movie.kt
data class Movie(
val Title: String,
val Year: String,
val Rated: String,
val Released: String,
val Runtime: String,
val Genre: String,
val Director: String,
val Writer: String,
val Actors: String,
val Plot: String,
val Language: String,
val Country: String,
val Awards: String,
val Poster: String,
val Ratings: List<Rating>,
val Metascore: String,
val imdbRating: String,
val imdbVotes: String,
val imdbID: String,
val Type: String,
val Dvd: String,
val boxOffice: String,
val Production: String,
val Website: String,
val Response: String
)

TypeConverter not working for android Room DB

So I am trying to save a list of string pairs into my database but having some issues with the TypeConverter, I tried following guides ans other SO posts but can't figure out whats wrong...
My Entity:
#Entity
data class Credential(
#PrimaryKey()
val id: String,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "url")
val url: String?,
#TypeConverters(ListPairTypeConverter::class)
#ColumnInfo(name = "fields")
val fields: List<Pair<String, String>>
)
My Type Converter:
class ListPairTypeConverter {
#TypeConverter
fun storedStringToPairList(value: String): List<Pair<String, String>> {
return value.split("~!!!!!~").map {
val vals = it.split("!~~~~~!")
Pair(vals[0], vals[1])
}
}
#TypeConverter
fun pairListToStoredString(pl: List<Pair<String, String>>): String {
return pl.joinToString(separator = "~!!!!!~") { it.first + "!~~~~~!" + it.second }
}
}
My Error:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private final java.util.List<kotlin.Pair<java.lang.String, java.lang.String>> fields = null;
^
You are adding type converter at wrong place. instead of
#TypeConverters(ListPairTypeConverter::class)
#ColumnInfo(name = "fields")
val fields: List<Pair<String, String>>
You need to add at here
#Database(entities = [User::class], version = 1)
#TypeConverters(ListPairTypeConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

Entity classes have some issues while using retrofit

I am getting this error while getting a response from an API.
Sometimes, it works fine but 8/10 times it crashes and shows me this error
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
at com.example.newsapi.db.Entities.Source.hashCode(Unknown Source:2)
at com.example.newsapi.db.Entities.Article.hashCode(Unknown Source:50)
This is the Article entity
#Entity(tableName = "Article_Table")
data class Article(
#PrimaryKey(autoGenerate = true)
val id: Int?=null,
val author: String,
val content: String,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
): Serializable
This is the Source class declared in the Article class
data class Source(
val id: String,
val name: String
)
Here is where I'm trying to put that Article object inside a Bundle
setOnClickListener {
val bundle = Bundle().apply {
putSerializable("article", currentArticle)
}
when (from) {
"BreakingNewsFragment" -> findNavController().navigate(R.id.action_breakingNewsFragment_to_articleFragment, bundle)
"SearchNewsFragment" -> findNavController().navigate(R.id.action_searchNewsFragment_to_articleFragment, bundle)
"SavedNewsFragment" -> findNavController().navigate(R.id.action_savedNewsFragment_to_articleFragment, bundle)
}
}
Here is the API call function
#GET("v2/top-headlines")
suspend fun getBreakingNews(
#Query("country")
countryCode: String = "us",
#Query("page")
pageNumber : Int = 1,
#Query("apiKey")
apiKey: String = API_KEY
): Response<NewsResponse>
calling that function from the repository
suspend fun getBreakingNews(countryCode: String, pageNumber: Int): Response<NewsResponse> {
return RetrofitInstance.api.getBreakingNews(
countryCode = countryCode,
pageNumber = pageNumber
)
}
calling that repository function from the viewmodel
fun getBreakingNews(countryCode: String){
viewModelScope.launch(Dispatchers.IO) {
breakingNews.postValue(Resource.Loading())
val response = articleRepository.getBreakingNews(countryCode, breakingNewsPage)
breakingNews.postValue(handleBreakingNewsResponse(response))
}
}
Here is the NewsResponse Class
data class NewsResponse(
val articles: List<Article>,
val status: String,
val totalResults: Int
)