Java generics с "семантикой", возможно ли это?
Сначала извиняюсь, если я использую неправильный термин, выбрав слово "семантика".
Я большой поклонник дженериков в Java по всем очевидным причинам. Это очень помогает мне, когда я работаю с огромным количеством нечетных битов кода, и мне часто приходится возвращаться к старым вещам. Сегодня я столкнулся с ошибкой классической ошибки, которую я, вероятно, не написал бы в предродовые дни, - они сделали меня немного ленивым.
Я хотел бы знать, есть ли языковая функция, либо на Java, либо, возможно, как аналогичная концепция на других языках, которая берет на себя безопасность типов дженериков и расширяет ее до своего рода семантической безопасности. В частности, я хочу помочь избежать ошибок, возникающих из-за неправильной установки.
Простой пример:
Map<String, String> myMap = new HashMap<String, String>();
String myKey = "key";
String myVal = "value";
myMap.put(myVal, myKey);
Какой компилятор не поймает. Я мог бы subclass String
сделать обертками, чтобы дать мне тип Key
и тип Value
, я полагаю. Я могу назвать свои переменные, чтобы указать их использование (как я сделал в этом примере). Что еще?
Итак, мои вопросы:
- из академического интереса, что называется этой концепцией и какие языки имеют эту функцию?
- В отсутствие какой-либо такой языковой функции, каковы наилучшие методы, чтобы избежать такой ошибки?
Спасибо
Ответы
Ответ 1
Я всегда использовал карты, которые имеют два разных типа для ключа и значения, никогда не имеют одинаковых типов. Я предполагаю, что есть моменты, когда вы хотели бы использовать один и тот же тип для ключа и значения, как в вашем примере. Я не могу думать о каких-либо языках с моей головы, которые имеют эту функцию.
На самом деле я использовал Clojure до и для карт, он использует ключевое слово для ключа, которое выглядит как :key
, а затем значение - это все, что угодно. Это делает его более ясным, но теперь я занимаюсь функциональным программированием, и это не вопрос, который вы задавали. Проверьте Lisp -подобные языки.
Я думаю, что это просто случай, когда они не смешиваются, Java, конечно, не может обеспечить такой контроль.
Лучшим советом было бы использовать разные типы для ключа и значения, чтобы компилятор мог обнаружить ошибку типа, или явно указывать переменные ключа и значения, как вы это делали.
Ответ 2
Вы не можете подклассифицировать String. Однако вы можете создавать свои собственные классы и использовать их. Например, если вы сопоставляете Лица с Ролями, вы можете определить Person like
class Person {
String id;
String firstName;
String lastName;
...
}
и роль, подобная
class Role {
int id;
String name;
}
(Обратите внимание, что они должны определить метод equals и hashcode)
и иметь сопоставление от человека к роли типа:
Map<Person, Set<Role>> myMap = new HashMap<Person, Set<Role>>();
чтобы система типа применяла то, что вы можете поместить где (в отличие от того, чтобы иметь карту строк для наборов ints). Не уверен, что это называется, но "Доменные объекты" и "Абстрактные типы данных" кажутся взаимосвязанными.
В отсутствие проверки типов резервное копирование - это модульные тесты.
Ответ 3
Вы можете использовать более обширную систему типов, избегая использования одного и того же типа в списках параметров.
то есть.
Map<Key, Value> myMap = new HashMap<Key, Value>();
Key myKey = // some key
Value myVal = // some value
myMap.put(myVal, myKey); //compiler error
На каком-то уровне вам нужно объяснить концепции и то, как они взаимодействуют с операциями, чтобы проверить язык на что-либо, заинтересованы в том, есть ли альтернативные подходы там.
Ответ 4
Я не знаю ни одной языковой концепции, которая поймала бы это. В основном то, что вы просите, это компилятор для чтения ума. то есть.
- Вы хотите создать карту пар.
- Вы хотите (предположительно) использовать любое имя переменной по вашему выбору.
- Вы хотите, чтобы компилятор обнаружил, что вы передали аргументы в неправильном порядке.
Моя единственная мысль состоит в том, что язык может наложить синтаксическое ограничение, чтобы имитировать требуемое семантическое ограничение. Например, это может потребовать, чтобы вы назвали переменные, которые должны использоваться в качестве ключей "key_somename_", а также для значений. Немного улучшения, но это скорее ограничивает программиста.
Если вы действительно беспокоитесь об этом, вы можете в Java расширять Map, чтобы принимать только типы, реализующие фиктивный "ключ", и создавать подтипы, которые реализуют этот интерфейс каждый раз, когда вы хотите использовать класс ключей.
Ответ 5
Это очень дерьмово, но оно работает:
public static <K extends String,V extends String> void test() {
K key = (K) "foo";
V val = (V) "bar";
Map<K,V> map = new HashMap<K,V>();
map.put(key,val);
}
Теперь это немного интереснее:
// The whole code is using type parameters like this:
public static <K extends String,V extends String> void test(K key, V value) {
Map<K,V> map = new HashMap<K,V>();
map.put(key,val);
}
// At the end, use the real type (K=String, V=String);
public static void main(String[) args) {
test<String,String>("foo","bar");
};