Блог о программировании

Январь 22, 2012

 

Про интерфейс: Map<K,V>

Недавно от товарища пришел вопрос на засыпку: "Почему в интерфейсe java.util.Map метод get в качестве ключа принимает Object, а метод put использует женерики?"

V get(Object key); V put(K key, V value);

Сходу ответить не смог, пришлось разбираться.
Сразу пришло на ум несколько гипотез:

  • раздолбайство
  • легаси-код
  • "так специально задумано"

Не смотря на то, что первые две гипотезы вполне правдоподобны, думаю самый интересный это третий вариант ("так специально задумано"). Если поискать в интернете,то можно найти следующее объяснение.

По "контракту" Map должен работать с ключами по следующему правилу:

key==null ? k==null : key.equals(k)

Легко видеть, что т.к. в при вызове метода key.equals(k) объекты key и k потенциально могут быть абсолютно разных классов, то в итоге мы не имеем право вводить ограничение на тип в сигнатуре метода.

Конечно большинство java-программистов знают, что если нужно переопределить метод equals, то по хорошему надо сделать проверку типа проверяемого объекта, но в жизни всякое случается...

В качестве примера к объяснению, в интернете можно встретить пример на списках.

Суть очень простая. Мы предполагаем, что списки равны тогда, когда равны все их элементы. При этом по большому счету не так уж и важно какой именно это список (ArrayList, LinkedList и т.д.).
В итоге если использовать список в качестве ключа, то возникнет следующая (вполне реальная) ситуация.

Map<LinkedList, Integer> m = new HashMap<LinkedList, Integer>(); LinkedList a = new LinkedList(); a.add("1"); a.add("2"); m.put(a, a.size());   // конечно здесь мы явно создаем ArrayList, // но впринципе нам может вернуться какой-то непонятный список // из какого-то непонятного метода: // например List b = doVeryStrangeMethod(); List b = new ArrayList(a); // Этот вызов метода нормально компилируется и вернет результат: System.out.println(m.get(b)); // А вот этот вызов приведет к ошибке компиляции, т.к. put с generic-ом // m.put(b, b.size());

P.S.: Лично я в жизни не сталкивался с ситуацией, когда нужно было использовать список в качестве ключа.

Блог о программировании

Январь 6, 2012

 

Java Zip + русские буквы в названиях файлов

Баг №4244499

Этот баг был зарегистрирован еще в 1999г.
Проблема была не только с русскими файлами, а например с французскими (как в примере).
Корни довольно глубокие и существовали серьезные (надеюсь) причины, по которым эту ошибку долгое время не исправляли. В итоге, много-много лет в Java нельзя было сделать простыми штатными средствами zip-архив, который бы содержал файлы с именами не на латинице.

К сожалению, жизнь такова, что все это время некоторых java-программистов заставляли делать такие zip-архивы. Приходилось использовать например apache-вский commons-compress.

Использовать очень просто. Алгоритм буквально в два шага:
Шаг 1. Загружаем jar-ик (ссылка) или просто добавляем в pom.xml зависимость (если maven) :

<dependencies> .... <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.3</version> </dependency> </dependencies>

Шаг 2. Используем:

import java.io.File; import java.io.FileInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import static org.apache.commons.compress.utils.IOUtils.copy; /* * ТОЛЬКО В ОЗНАКОМИТЕЛЬНЫХ ЦЕЛЯХ! * Пример очень небрежный, в реальных задачах код нужно причесать! * Author: Vit */ public class Sample {   public static void main(String[] args) throws Exception { File srcDir = new File("c:/temp/dir"); // список файлов File[] files = srcDir.listFiles(); File zipFile = new File(srcDir, srcDir.getName() + ".zip"); // архив ZipArchiveOutputStream zaos = new ZipArchiveOutputStream(zipFile); for (File itemFile : files) { ZipArchiveEntry entry = new ZipArchiveEntry(itemFile.getName()); zaos.putArchiveEntry(entry); FileInputStream fis = new FileInputStream(itemFile); try { // копируем потоки copy(fis, zaos); } finally { fis.close(); } zaos.closeArchiveEntry(); } zaos.close(); } }

Счастливый конец.

В текущей версии JDK 7 этого бага уже нет.
Во-первых в семерке в ZIP API добавили новый конструктор, в котором можно явно указывать кодировку:
ZipOutputStream(OutputStream out,Charset charset)

Во-вторых, по-умолчанию используется стандартная UTF-8 кодировка.
Таким образом, дополнительных библиотек использовать не нужно, можно сразу делать zip-архив:

/* * ТОЛЬКО В ОЗНАКОМИТЕЛЬНЫХ ЦЕЛЯХ! * Пример очень небрежный, в реальных задачах код нужно причесать! * Author: Vit */ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream;   public class Sample { public static void main(String[] args) throws Exception { File srcDir = new File("c:/temp/dir"); File[] files = srcDir.listFiles(); File zipFile = new File(srcDir, srcDir.getName() + "_j7.zip"); FileOutputStream fos = new FileOutputStream(zipFile);   try (ZipOutputStream zos = new ZipOutputStream(fos)) { for (File itemFile : files) { ZipEntry entry = new ZipEntry(itemFile.getName()); zos.putNextEntry(entry);   try (FileInputStream fis = new FileInputStream(itemFile)) { byte[] buff = new byte[8192]; int length = -1; while (-1 != (length = fis.read(buff))) { zos.write(buff, 0, length); } } zos.closeEntry(); } } } }

Блог о программировании

Декабрь 15, 2011

 

Немного об OAuth, Android и Facebook

OAuth авторизация

OAuth - по определению означает Open Authorization. Поскольку в английском языке слова аутентификация (authentication) и авторизация (authorization) имеют одинаковое начало auth, то сокращение oauth очень неоднозначное. Эти понятия (авторизацию и аутентифакцию) очень часто путают друг с другом.
Например OpenID - это система для аутентификации.

Очень кратко опишу про аутентификацию и авторизацию (т.к. это простые и занудные понятия).

Если примитивно, то аутентификация - это, например, когда на вахте человек показывает пропуск или удостоверение, а вахтер смотрит и соображает "пропуск настоящий или нет? правильная печать? фотка совпадает? и т.д. ".
Другими словами проверяет валидный пропуск или нет.
Аутентификация может быть простой (логин/пароль), а может быть и очень замысловатой. Например бывалые вахтеры часто используют Face Recognition Authentication.

На веб-сайтах за вахтера эту работу осуществляет модуль аутентификации, который заставляет нас вводить логин и пароль, а затем проверяет их правильность.

После успешной аутентификации, наступает процесс авторизации.
Например, вахтер понял - пропуск настоящий, а вот можно ли пускать этого человека в здание или нет?

Например вполне может быть следующая ситуация - в выходной день в здание можно попасть только боссу или охраннику, а предъявитель пропуска - обычный человек, которого даже нет в списках работающих по выходным (или после 22-00)! Так что успешно пройденная аутентификация еще не значит, что человек сможет пройти туда, куда хотел.

Другими словами, мы при авторизации "даем добро" на определенные действия, при этом на момент авторизации (как правило) самого пользователя мы уже отаутентифицировали.
Например, в нашем случае с вахтером успешной "авторизации" человеку выдают например пенал с ключами и он пойдет по своим делам.
При этом, важно то, что в результате проделанной вахтером авторизации, человеку выдаются только определенные permissions.

Таким образом, используя OAuth мы можем определять к чему будет доступ у аутентифицированного пользователя.
Например, мы можем ограничить доступ к только общей информацию или же предоставить доступ к возможности отправлять от своего имени сообщения, менять статус, получить к информации в любое время и т.д.

Другими словами, если открытая аутентификация - это когда мы отдаем на сторону проверка логина+пароля,
то при открытой авторизации мы уже можем делать больше - управлять разрешениями.

Facebook

OAuth поддерживает Facebook и Twitter.

Общая схема работы следующая:

1. Отсылаем браузер на страницу аутентификации (допустим на Facebook или Twitter).
2. Пользователь видит родную фейсбучную страничку и не боясь вводит логин/пароль.

(картинка с сайта facebook-а)
3. Если все ОК. показывает страничку в которой спрашивает: "Дать право доступа к функции X?"

4. Если все ОК, браузер редиректит обратно, на URL нашего веб-сайта (какой именно URL мы указываем в п.1).
5. Обрабатываем редирект. В параметре редирект запроса к нам приходит "код доступа".
6. С кодом доступа и секретом, отправляем запрос на получение токена (ACCESS_TOKEN).
7. Отправляем прикладные запросы с токеном (например получить информацию о пользователе: https://graph.facebook.com/me?access_token=ACCESS_TOKEN ).

На фейсбуке всё очень подробно показано и рассказано: http://developers.facebook.com/docs/authentication/

Схема работы:

Android

В случае если вы разрабатываете приложение под Андроид, то засада на редиректе (шаг 4).

Например, в случае приложения под Android:
0. Нажимаете кнопку авторизовать.
1. Стартуем Activity для обработки Intent-а с нужным URL-ом для авторизации.
2. Открывается встроенный браузер.
3. Вводим логин/пароль и ....

Дальше возникает вопрос, как нам вернуться назад, в наш исходный Activity?
Основная хитрость redirect URL. Мы придумываем его в виде, вроде myapp://success и настраиваем наше Activity
так, что именно оно должно уметь отлавливать такие урлы:

<intent-filter> ... <data android:scheme="myapp" android:host="success" /> </intent-filter>

В итоге, после успешной авторизации браузер поймет, что нужно запустить Activity, которое может обработать этот странный URL "myapp://success".

А теперь вся правда в деталях.
1. Обязательно нужно указать в intent-filter, что бы Activity отлавливала запросы от браузера.

Подробнее читать здесь: http://developer.android.com/reference/android/content/Intent.html#CATEGORY_BROWSABLE

<intent-filter> ... <category android:name="android.intent.category.BROWSABLE" /> ... <intent-filter>

2. Чтобы не мучится, можно указать что Activity - singleInstance.

<activity ... android:launchMode="singleInstance">

Тогда обработку запроса можно будет безболезненно сделать в переопределенном методе onNewIntent.

3. Важно! Facebook не даст вставлять какие попало URL-ы!
Так что придется брать строчку из примера: REDIRECT_URI = "fbconnect://success"

Еще раз, указывать надо не "myapp://", а именно "fbconnect://".

4. Далее с Facebook, ситуация такая. Использовать библиотеки signpost смысла нет, замучаетесь прикручивать.
Для Twitter-а signpost работает нормально, примеры использования растиражированы на многих блогах.

Самый правильный способ - использовать готовое Facebook API для андроида.

Очень подробная инструкция в картинках есть на сайте Facebook-а: https://developers.facebook.com/docs/mobile/android/build/

Если все-таки этот путь вам не подходит или вы слишком упрямый, нужно учесть еще следующее:

5. В случае с Facebook лучше указывать параметр display=touch.

Т.е. что-то вроде:
http://www.facebook.com/dialog/oauth?client_id=APP_ID&redirect_uri=fbconnect://success&display=touch

Тогда сразу будет компактная и удобная форма для аутентификации.

Java на habrahabr

Ноябрь 8, 2011

 

JAVA / Олимпиада по программированию на Java для студентов


Компания Oracle совместно с организацией It-планета приглашает студентов ВУЗов и колледжей принять участие в международной олимпиаде по программированию на Java — Oracle Java Olympic. Соревнования проходят в Украине, России, Беларуси и Казахстане и состоят из трех этапов. Для участия желательна регистрация ВУЗа или колледжа в системе, но возможно и персональное участие. Регистрация для участия в олимпиаде проходит до 30 ноября 2011 года на сайте http://world-it-planet.org/. Для регистрации на первой странице выберите «Oracle Java Olympic» и ваш регион проживания.

Первый этап — отборочный. Проводится удаленно. После регистрации участники получают ссылку для прохождения теста и в течении 45 минут отвечают на 40 вопросов по Java технологиям. Тест можно проходить в любое время с 1 по 15 декабря включительно. Для прохождения теста у каждого участника есть только одна попытка.

Второй этап
Победители отборочных этапов в своем ВУЗе приглашаются для участия в очном региональном туре, в ходе которого участники получают 7 задач, 4 часа времени и компьютер с операционной системой, JDK7, NetBeans 7.0.1 и документацией по Java API. Участие в соревнованиях личное.

Третий этап — финальный. Проводится в формате аналогичном второму этапу. Основное отличие — более сложные задачи (и соперники конечно).

Подробности и детальное описание конкурса смотрите на веб-сайте олимпиады.
http://world-it-planet.org/

Присоединяйтесь уже сегодня!

Блог о программировании

Сентябрь 23, 2011

 

Кофе и программисты

Мой небольшой обзор разных кофеен.

Уже больше года веду образ жизни свободного разработчика. Время от времени встречаюсь с такими же как я (программистами, стартаперами). На таких встречах мы, как правило, обсуждаем текущие задачи, пьём кофе, строим планы на будущее, а иногда устраиваем "полевые испытания" - вместе проверяем как работает веб-сервер, тестируем приложения под андроид в суровой реальности, а не в тепличных условиях и т.д.
За время этих встреч у меня появился свой (очень субъективный) мини-рейтинг различных кофеен с точки зрения их удобства для программистских тусовок.

Кофе Хауз на Никольской

Кофе Хауз. Ст. м. Площадь Революции (фото с сайта www.intomoscow.ru)

Один мой друг сказал мне, что для кафе есть золотое правило "трех L" - Location, Location и еще раз Location. Наверное, в этом есть какая-то доля истины, т.к. Location (место расположения) - основная причина, почему я бываю в этой кофейной чаще всего. Этот Кофе Хауз расположен в шаговой доступности от станции метро Площадь Революции. Если люди живут на разных ветках, то это место для встреч - почти идеальное. Конечно, на машине доехать сложно, но зато тем, кто добирается на метро, очень удобно.

Про кофе. На мой вкус - обычный, нормальный кофе. Пить можно. Цены на кофе недешевые, но есть кофейни намного дороже (см. ниже)!
Можно использовать законный лайфхак - в Кофе Хаузе постоянно дают флайеры на вторую чашку. Если их использовать, то цены уже нормальные Главное не забывать оставлять на чай и забирать флайер (правда мне кажется, что все-таки нет корреляции "чаевые - флайер").

Про интернет. Обычный WiFi, качество связи не очень (именно в этой кофейне). Одно время постоянно были разрывы. Необходимо обратить внимание на то, что не все порты открыты! Так что нужно будет заранее позаботиться проксей или перекинуть на 80-ый, если у вас что-то необычное.

Ну и самое главное (касается всех кофеен), поскольку сеть идет по воздуху и открытая - нужно не забывать предохраняться.
Как запасной вариант, если есть телефон с 3G + WiFi Hub (многие андроидные такое могут), то его можно использовать в качестве альтернативного канала. Важно! Это скорее всего не будет работать на минус первом этаже этого Кофе Хауза. Там меньше народу и уютней, но при этом не всегда ловит мобильный. На первом этаже - шумновато и тесновато, поскольку помещение не очень большое.

Несмотря на это, чисто мое субъективное мнение - кофейня хорошая! Если говорить про сеть в целом, то лично меня Кофе Хаузы постоянно спасают, когда нужно куда-нибудь укрыться, сделать передышку, выпить кофе, глянуть почту и собраться с мыслями.

Плюсы:

  • Расположение.
  • WiFi.
  • Нормальный кофе.

Минусы:

  • Дороговато (но я запасаюсь флайерами, так что все ОК).
  • Бывает шумно и многолюдно.

Старбакс на Покровке

Старбакс на Покровке (фото с сайта www.xlander.ru)

Больше всего в этой кофейне мне нравится открытая верандочка на втором этаже. Летом там просто волшебно!
К сожалению, она нравится не только мне и в ней, как правило, уже кто-то сидит. Еще плохо то, что эта кофейня расположена далеко от метро. А если ехать на машине, то в районе метро "Китай-город" постоянные пробки и проблемы с парковкой. Бываю я там не часто, а сейчас, осенью и дальше, буду совсем редко.

Цены выше, чем в "Кофе Хаузе", но не зашкаливают (как например, в "Кофемании").
WiFi вроде нормальный, но сразу оговорюсь - я там серьезного ничего не делал. Почту почитать и в интернете поискать что-нибудь.
Кофе вкусный. Уютно. Бывает настолько много людей, что на летнюю веранду не попасть, а в помещении - обычный, нормальный, стандартный Старбакс.

Плюсы:

  • WiFi.
  • Нормальный кофе.

Минусы:

  • Расположение. Далеко от метро.
  • Дорого.
  • Бывает людно, но здесь как повезет.

КофеМания на Покровке

КофеМания на Покровке (фото с сайта:coffeemania.ru)

Дорого! Кофе хороший, прямо очень хороший. Эспрессо действительно похож на то, что продают в Риме.
Но цены! Как будто кофе варят в Риме и телепортируют в Москву.
Место приятное, само помещение внутри "воздушное", нет давящего "подвального" ощущения.
Никогда не видел, чтобы там было мало посетителей и, как правило, в зале много народу. Поэтому шумно, но как ни странно, это не мешает.
С интернетом - не понятно. Вроде как был, потом как-то заходил - не было. Последний раз, вроде был. Если есть йота или 3g модем, то лучше прихватить.

Плюсы:

  • Хороший кофе.

Минусы:

  • Расположение. Далеко от метро.
  • Дорого. Очень.

КофеИн на Пушечной

КофеИн на Пушечной (фото с сайта: msk.allcafe.info)

Очень приятное место. Внутри уютно, очень удачно сделан второй свет. По-крайне мере, мне нравится именно на втором этаже. Одно время в этой кофейне мы с друзьями собирались на свои встречи группы энтузиастов, изучающих Scala (может, потом напишу про эти встречи тоже , сейчас мы перебазировались в другое место). Правда как-то столкнулись с проблемой - летом было невыносимо жарко в помещении. Пришлось искать другое место. Думаю сейчас, с приближением зимы уже не актуально.
В целом с кофе-ином у меня очень приятные ассоциации. Давным-давно, когда я был еще студентом, то бывал в кофейне с похожим названием. Она была расположена кажется на Б. Дмитровке. Место было очень душевное, многие прогулки по Москве у меня заканчивались именно там. Потом его закрыли. Возможно тот "кофе-ин" никакого отношения не имеет к этому, но само название пробуждает приятные воспоминания.
Про интернет. Качество нормальное, канал вполне широкий, разок мы даже совсем обнаглели и запустили видео-конференцию.
Расположение хорошее. Рядом несколько станций метро.
Кофе - очень даже ничего, вкусный. Цены высокие, но не настолько как в соседней Кофемании. Можно оформить себе карту, тогда, кажется, каждый пятый кофе - бесплатно.

Плюсы:

  • Вкусный кофе.
  • Неплохой WiFi.

Минусы:

  • Дорого. Не очень. Но все равно.
  • Летом, один раз, было очень жарко. Можно было бы потерпеть, но ноуты начинали греться.

Шоколадница в Долгопрудном

Шоколадница в Долгопрудном (фото с сайта: gdevgorode.ru)

Да, наверное вы не ожидали, но  это кофейня в Долгопрудном. Мне приходится здесь бывать, т.к. здесь находится физтех (МФТИ). В Долгопрудном нет стартбакса, кофе-хауза и других известных кофеен. Поэтому то, что здесь есть Шоколадница, это очень круто!
Во-первых наличие "Шоколадницы" несколько смягчает культурный шок у людей, которые первый раз приезжают в этот город (известный бренд, понятное качество и т.д.).
Во-вторых - кофейня расположена недалеко от железнодорожной и автобусной станций, что удобно (правило 3L здесь тоже рулит)!
В-третьих - уровень обслуживания не отличается от московских Шоколадниц.
В-четвертых - есть WiFi (не забываем, что не в Москве).

Если говорить про сеть в целом, то в Шоколаднице мне стало нравиться. Помню, в первой половине двухтысячных было ужасно. Ждать кофе можно было по полчаса и больше. При внешней опрятности заведения уровень сервиса был слабым. Помню, многие мои друзья начинали "избегать" именно этой кофейни.
Потом как-то неожиданно для себя, я заметил, что ситуация резко начала меняться. Так что сейчас вообще никто ничего такого не помнит.
Кофе нормальный. Уровень цен как в Кофе Хаузе (без учета флайеров). Насчет карт клиента и флайеров не знаю, по крайне мере ничего такого не предлагают.
Интернет обычный (правда один раз не повезло и интернет не работал. В итоге, в тот день, я его нигде не смог найти в этом городе).
В целом неплохая кофейня, если будете в Долгопрудном - довольно неплохой вариант.

Плюсы:

  • Единственная брендовая кофейня в Долгопрудном.
  • WiFi.

Минусы:

  • Никаких (с учетом того, что альтернатив здесь нет).

Блог о программировании

Июль 15, 2011

 

Scala и пустота

Scala и пустота

В Scala есть несколько разных сущностей для обозначения несуществующих, пустых или неопределенных объектов.
Начнем с самого привычного для Java-программистов случая.

Null

Null – это trait. Объект null (с маленькой буквы) — это как раз и есть объект типа Null. Он находится внизу иерархии типов в Scala, в противовес AnyRef.
Благодаря этому вы всегда можете как-бы "занулить" любую ссылку, т.е. присвоить ссылки значение null:

scala> var x = "String" x: java.lang.String = String scala> var i = List(3) i: List[Int] = List(3) scala> i = null i: List[Int] = null scala> x = null x: java.lang.String = null

Null работает только для ссылочных типов, для более общего случая, есть "ничто (Nothing)" .

Nothing

Nothing – это тоже trait. Nothing находится на самом дне иерархии типов, в противовес Any. Соответственно, это более общий тип, чем Null и подходит даже для AnyVal объектов (числа, буквы, правда/ложь и т.д.).

В отличие от Null, Nothing не может иметь экземпляров (на то оно и ничто).
Другими словами нет аналога null для Nothing.

Возникает вопрос, где такое самое "нижнее ничто" может использоваться?
В документации по API можно найти несколько примеров:

// пакет scala.sys def error(message: String): Nothing = throw new RuntimeException(message)

Этот метод никогда ничего не возвращает, поэтому возвращаемый тип Nothing.

Unit

Тип Unit – чем-то похоже на void, который исползуется в Java. В Scala тип Unit используем тогда, когда хотим показать, что функция возвращает пустое значение (но все-таки что-то возвращает, хоть и пустое).

Если открыть документацию, то можно увидеть что:

class Unit extends Any with AnyVal

Внимание! AnyVal, а не AnyRef. Это значит, что Unit это не ссылочный тип ( в отличие от Null). Можно сказать, что Unit-у как бы "ближе по родству" будут числа, буквы и другие примитивные типы (которые тоже AnyVal).

В отличие от Nothing, Unit повезло больше. Он может иметь свой объект, правда в единственном экземпляре. Он обозначается двумя круглыми скобками: (). Например:

scala> val u = () u: Unit = ()

Другими словами Nothing уместен тогда, когда функция в принципе ничего не возвращает, а Unit – это когда возвращает, но оно пустое.
Это отличие существенно. Например результат вызова функции с Unit может быть присвоено (в Java с void такой фокус не выйдет).

scala> def foo():Unit = {} foo: ()Unit scala> val u = foo() u: Unit = ()

Nil

Nil – пустой список (extends List[Nothing]).
Поскольку Nil – это список, хоть и пустой, у него как у любого списка есть метод :: (два двоеточия), с помощью которого удобно создавать списки:

scala> var x = 1 :: 2 :: Nil x: List[Int] = List(1, 2)

Это возможно благодаря тому, что название метода заканчивается на : (двоеточие), в таком случае метод применяется к правому операнду (работает для операторной нотации вызова метода).

Другими словами, это аналогично вызову: (Nil.::(2)).::(1)

None

None – это такой хитрый объект, который используется в случае, если мы хотим получить что-то, например, из Map, а его там нет.

Например:

scala> var m = Map(1->2) m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2) scala> val n = m.get(100)// ну нет такого элемента n: Option[Int] = None

None примечателен тем, что:

  • это объект (т.е. синглтон)
  • это кейс-объект
  • наследуется от Option[Nothing]

В случае попытки получить значение (вызвав метод n.get()), мы получим исключение:java.util.NoSuchElementException.

Если у None вызывать метод isEmpty() – мы получим true.

Поскольку None – объект кейс-класса, то мы можем его "матчить" (сопоставлять по шаблону).

scala> n match { case Some(x) => println("x:" + x) case None => println("none") }

Если у None вызвать метод toList – мы получим пустой список (т.е. Nil).

scala> n.toList() == Nil res21: Boolean = true

Поскольку None объявлен как extends Option[Nothing], а Nothing – "самое нижнее ничто", то None может работать с любыми типами (как с ссылочными так и с примитивными).

Java на habrahabr

Июнь 26, 2011

 

JAVA / [Из песочницы] Получаем доступ из Java в JavaScript через swt браузер

Хоббит или туда и обратно


Некоторое кол-во времени назад мне срочно потребовалось найти возможные способы получения переменных из js и браузера, чтобы передать их значение в моем приложении на java. К сожалению, на хабре данный вопрос не был раскрыт или возможно сила покинула меня и поиск дается уже не так легко.
Как бы то ни было сегодня я постараюсь в какой то мере пролить свет на данный вопрос. Итак приступим!

Блог о программировании

Июнь 1, 2011

 

Немного карри

Использовать карринг в Scala одно удовольствие.
Само определение каррирования(или карринга, названо в честь Хаскелла Карри):

Для функции h типа h : (A × B) → C оператор каррирования Λ выполняет преобразование
Λ(h) : A → (B → C)

Отметим, что Λ - это оператор, то есть "функция над функциями".
Например берем функцию foo(x,y), каррируем и получаем moo(x)(y) - функцию от x которая возвращает функцию от y.

Таким образом мы можем сводим программу к вычислению функции от одного аргумента.

Чтобы выполнить карирование, можно вызвать Function.curried. Сейчас это уже устаревший метод, т.к. curried можно вызывать сразу у функции.

Небольшой пример (для наглядности в императивном стиле):

scala> val foo = (x:Int,y:Int) => x + y // функция от 2(ДВУХ) аргументов x и y. scala> foo(1,2) // просто сложили и получили три scala> val moo = foo.curried // получили функцию от 1(ОДНОГО) аргумента, которая возвращает scala> val zoo = moo(1) // функцию от 1(ОДНОГО) аргумента scala> zoo(2) // урраа! получили три scala> // можно каррировать и так: scala> def noo(x:Int, y:Int) = x * y scala> val a = noo _ // получили функцию от 2-х аргументов (подробнее см. partially applied function) scala> val b = a.curried scala> b(2) // = фунция 2 * аргумент scala> b(2)(3) // = 6

Метод curried можно найти в трейтах начиная от Function2 и заканчивая Function22.
Это значит, что вы можете использовать его для функций от 2 аргументов и до функций с 22-аргументами включительно.

Для функций без аргументов и с одним аргументом использовать бессмысленно.

Для функций с более чем 22-мя не поддерживается (таких трейтов вообще нет).
Например если Вы захотите сделать:
val foo = (x0:Int,x1:Int и т.д. до x22:Int) => 0
то получите ошибку компиляции:
"implementation restricts functions to 22 parameters".

Чтобы не возникало путаницы, поясню.

  • Во-первых:
    Это не значит, что вы не можете объявлять методы
    def foo(x0:Int,x1:Int и т.д. до x22:Int) = 0
    Это будет работать, но использовать в полную "силу" возможности Scala по работе с функциями уже не получится.
    Можно считать, что это "обычный" метод с 23 аргументами...
  • Во-вторых:
    Функции с varargs (переменным количество аргументов) работают нормально. Здесь никаких проблем с каррирование быть не должно.
  • В-третьих:
    Если у вас функции с более чем 22 аргументами это π#_!@#?!!!
    Думаете такого не бывает? Когда-то я работал в организации, в которой нужно было писать и разбираться со скриптами на лиспе (было и такое в моей жизни). Так вот, некоторые умельцы декларировали функции от 15-ти и более аргументов.
    Я в очередной раз взываю ко всем программистам с планеты Земля.
    Пожалуйста, постарайтесь так больше не делать. Уж очень неприятно работать с таким кодом.

Блог о программировании

Май 5, 2011

 

Календари и даты

Постоянно наталкиваюсь на людей, которые утверждают, что "Date - deprecated, нужно использовать Calendar".
Это не верно! Вариаций на тему "почему это не верно" где-только уже не написано, но все-равно появляются люди которые мне доказывают, что правильней использовать Calendar (аргументеруя именно "устареванием" Date).

На самом деле в классе Date просто много deprecated методов. Но если почитать внимательно документацию, то видно, что устарел не сам класс, а просто часть функций этого класса еще во времена выхода Java 1.1 была вынесена в другой класс. Вот и всё!

Целевое назначение объектов класса Date сохранилось. Это простой и легкий класс для обозначения времени (например 2010-01-01T12:00:00). Он сериализуется, и если вам нужно хранить именно дату (и время) в каком-то классе, то в большинстве случаев, разумнее всего использовать именно Date.

Что касается класса Calendar , то это в первую очередь календарик. Он нужен для того, чтобы узнать например какой день недели был на указанную дату, сколько дней прошло с начало года и т.д.. Такой своеобразный аналог обычного настенного календаря с курсором. И гонять его между разными слоями/тирами приложения в составе трансферных объектов или делать его атрибутом какого-то класса для обозначения даты-времени не логично (в большинстве случаев).

Если вам нужно отформатировать или распарсить дату, вы можете использовать например SimpleDateFormat.

Кстати о SimpleDateFormat. Многие его тоже используют не правильно. Постоянно наталкиваюсь в чужих исходниках на конструкции типа:

// так лучше не делать! private final static SimpleDateFormat DDMMYYYY = new SimpleDateFormat("dd.MM.yyyy");

Конечно дело не в том, что он статик! Просто SimpleDateFormat он в общем-то не потокобезопасен. Как написано в документации:

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

То есть по хорошему, если это статик поле будут конкурентно использоваться, то можно нарваться на неприятности. Как правило такие баги (когда игнорируют многопоточное выполнение) не ловятся юнит-тестами, зато в продакшене могут создавать проблемы.

P.S.: Интересно, но использование Calendar вместо Date можно встретить даже в таком матёром API как JCR. Вот не знаю, почему они используют Calendar, а не Date...

Блог о программировании

Март 6, 2011

 

Удобный небезопасный кастинг

Удобный автоматический кастинг

Вещь не новая, но для тех, кто начинал программировать на Java в 90-ые и не очень привык к Generics-ам, это может быть интересно.

Пример использования. Вам нужен метод, который возвращает объекты разных классов. Это могут быть обычные объекты – String, Integer, List, а могут быть и какие-то "свои" сложные бизнес-объекты вроде – ABController, XYZComponent, QWERTYHelper, FooManager (название зависит от возраста и предпочтений программиста).

Так писали некоторые до Java 5.

Helper helper = ....; // Какой-то вспомогательный класс, который хранит всякую ерунду   String path = (String) helper.get("storage.path"); // да, я знаю, хардкодить константы плохо. Integer maxSize = (Integer) helper.get("max.size"); MyAwesomeDocumentManager manager = (MyAwesomeDocumentManager) helper.get("manager");

Сейчас можно писать попроще. Конечно это сахар, но код выглядит полегче:

String  path = helper.get("storage.path"); Integer maxSize  = helper.get("max.size"); MyAwesomeDocumentManager manager = helper.get("manager");

Как это делается:

public class Helper {   public <T> T get(String key) {   Object result = null; // Здесь должа быть спрятана какая-то страшная внутренняя реализация // например: result = map.get(key); return (T) result; } }

Принципиально ничего не меняется, только кастинг перешел во внутрь.
Если нужен статик, то можно написать так:

public static <T> T get(String key) { Object result; // опять какая-то страшная логика // типа: result = map.get(key); return (T) result; }

 
<< < 1 из 3 > >>